@ 260915.5

211220.3 · cc8668306692ed28dae7007ed24bd4f4df6addae

released 3.4.0 new options --files and --lines for --bool searching

reading declarations

README.md

@@ -1,6 +1,6 @@ [![build status][travis-image]][travis-url] [![Language grade: C/C++][lgtm-image]][lgtm-url] [![license][bsd-3-image]][bsd-3-url] -**ugrep v3.3 is now available: more features & even faster than before**+**ugrep v3.4 is now available: more features & even faster than before**  Search for anything in everything... ultra fast @@ -30,7 +30,7 @@ Search for anything in everything... ultra fast        ugrep -Z PATTERN ...                   ugrep -Z3 PATTTERN ... -- Search with Google-like [Boolean search patterns](#bool) with option `--bool` patterns with `AND` (or just space), `OR` (or a bar `|`), `NOT` (or a dash `-`), using quotes to match exactly, and grouping with `( )`; or with options `-e` (as an "or"), `--and`, `--andnot`, and `--not` regex patterns+- Search with Google-like [Boolean query patterns](#bool) using `--bool` patterns with `AND` (or just space), `OR` (or a bar `|`), `NOT` (or a dash `-`), using quotes to match exactly, and grouping with `( )`; or with options `-e` (as an "or"), `--and`, `--andnot`, and `--not` regex patterns        ugrep --bool 'PATT1 PATT2 PATT3' ...   ugrep -e PATT1 --and PATT2 --and PATT3 ...       ugrep --bool 'PATT1|PATT2 PATT3' ...   ugrep -e PATT1 -e PATT2 --and PATT3 ...@@ -38,9 +38,12 @@ Search for anything in everything... ultra fast       ugrep --bool 'PATT1 -(PATT2|PATT3)'... ugrep -e PATT1 --andnot PATT2 --andnot PATT3 ...       ugrep --bool '"PATT1" "PATT2"' ...     ugrep -e '\QPATT1\E' --and '\QPATT2\E' ... +  Boolean query conditions apply to lines by default (grep is a line matching tool!)  Specify `--files --bool` to apply the Boolean query to files as a whole: a file matches if all Boolean conditions are satisfied by matching patterns anywhere in the file.+ - Fzf-like search with regex (or fixed strings with `-F`), fuzzy matching with up to 4 extra characters with `-Z+4`, and words only with `-w`, press TAB and ALT-y to view a file, SHIFT-TAB and Alt-l to go back to view the list of matching files ordered by best match        ugrep -Q1 --bool -l -w -Z+4 --sort=best+      ugrep -Q1 --files --bool -l -w -Z+4 --sort=best  - Search the contents of [archives](#archives) (cpio, jar, tar, pax, zip) and [compressed files](#archives) (zip, gz, Z, bz, bz2, lzma, xz, lz4, zstd) @@ -51,6 +54,7 @@ Search for anything in everything... ultra fast       ugrep --filter='pdf:pdftotext % -' PATTERN ...       ugrep --filter='odt,doc,docx,rtf,xls,xlsx,ppt,pptx:soffice --headless --cat %' PATTERN ...       ugrep --filter='pem:openssl x509 -text,cer,crt,der:openssl x509 -text -inform der' PATTERN ...+      ugrep --filter='latin1:iconv -f LATIN1 -t UTF-8' PATTERN ...  - Search [binary files](#binary) and display hexdumps with binary pattern matches (Unicode text or raw byte patterns) @@ -131,7 +135,7 @@ Table of contents   - [Configuration files](#config)   - [Interactive search with -Q](#query)   - [Recursively list matching files with -l, -R, -r, --depth, -g, -O, and -t](#recursion)-  - [Boolean search patterns with --bool (-%), --and, --not](#bool)+  - [Boolean query patterns with --bool (-%), --and, --not](#bool)   - [Search this but not that with -v, -e, -N, -f, -L, -w, -x](#not)   - [Search non-Unicode files with --encoding](#encoding)   - [Matching multiple lines of text](#multiline)@@ -1308,10 +1312,10 @@ To recursively list all shell scripts based on extensions only with `-tshell`:  <a name="bool"/> -### Boolean search patterns with --bool (-%), --and, --not+### Boolean query patterns with --bool (-%), --and, --not      --bool, -%-            Specifies Boolean search patterns.  A Boolean search pattern is+            Specifies Boolean query patterns.  A Boolean query pattern is             composed of `AND', `OR', `NOT' operators and grouping with `(' `)'.             Spacing between subpatterns is the same as `AND', `|' is the same             as `OR', and a `-' is the same as `NOT'.  The `OR' operator binds@@ -1325,12 +1329,24 @@ To recursively list all shell scripts based on extensions only with `-tshell`:             lines with `A' and also either `AND' or `OR'.  Parenthesis are used             for grouping.  For example, --bool '(A B)|C' matches lines with `A'             and `B', or lines with `C'.  Note that all subpatterns in a Boolean-            search pattern are regular expressions, unless option -F is used.+            query pattern are regular expressions, unless option -F is used.             Options -E, -F, -G, -P, and -Z can be combined with --bool to match             subpatterns as strings or regular expressions (-E is the default.)             This option does not apply to -f FILE patterns.  Option --stats             displays the search patterns applied.  See also options --and,-            --andnot, and --not.+            --andnot, --not, --files, and --lines.+    --files+            Apply Boolean queries to match files, the opposite of --lines.  A+            file matches if all Boolean conditions are satisfied by the lines+            matched in the file.  For example, --files -e A --and -e B -e C+            --andnot -e D matches a file if some lines match `A' and some lines+            match (`B' or `C') and no line in the file matches `D'.  May also+            be specified as --files --bool 'A B|C -D'.  Option -v cannot be+            specified with --files.  See also options --and, --andnot, --not,+            --bool and --lines.+    --lines+            Apply Boolean queries to match lines, the opposite of --files.+            This is the default Boolean query mode to match specific lines.     --and [[-e] PATTERN] ... -e PATTERN             Specify additional patterns to match.  Patterns must be specified             with -e.  Each -e PATTERN following this option is considered an@@ -1354,8 +1370,8 @@ To recursively list all shell scripts based on extensions only with `-tshell`:  Note that the `--and`, `--not`, and `--andnot` options require `-e PATTERN`. -The `--bool` option makes all patterns Boolean expressions supporting the-following operations:+The `--bool` option makes all patterns Boolean-based, supporting the following+logical operations:  operator | alternative | result -------- | ----------- | -------@@ -1374,7 +1390,7 @@ operator | alternative | result - `-` and `NOT` are the same and take precedence over `OR`, which means that   `-x|y` == `(-x)|y` for example. -The `--stats` option displays the Boolean search query in human-readable form+The `--stats` option displays the Boolean queries in human-readable form converted to CNF (Conjunctive Normal Form), after the search is completed. To show the CNF without a search, read from standard input terminated by an EOF, like `echo | ugrep --bool '...' --stats`.@@ -1399,6 +1415,12 @@ and ALT-l to go back to the list of matching files:      ugrep -Q1 --bool -l -w -F -Z+4 --sort=best +To recursively find all files containing both `hot` and `dog` anywhere in the+file with option `--files`:++    ugrep --files --bool 'hot dog'+    ugrep --files -e hot --and dog+ To find lines containing both `hot` and `dog` in `myfile.txt`:      ugrep --bool 'hot dog' myfile.txt@@ -1497,6 +1519,9 @@ same line, like XOR:             -f FILE patterns to allow -f FILE patterns to narrow or widen the             scope of the PATTERN search. +See also [Boolean query patterns with --bool (-%), --and, --not](#bool) for+more powerful Boolean query options than the traditional GNU/BSD grep options.+ To display lines in file `myfile.sh` but not lines matching `^[ \t]*#`:      ugrep -v '^[ \t]*#' myfile.sh@@ -3569,100 +3594,101 @@ in markdown:                   matches lines with (`A' or `B') and (`C'  or  `D').   Note  that                   multiple  -e  PATTERN  are  alternations  that bind more tightly                   together than --and.  Option --stats displays  the  search  pat--                  terns applied.  See also options --not, --andnot, and --bool.+                  terns  applied.   See  also  options  --not,  --andnot,  --bool,+                  --files, and --lines.             --andnot [[-e] PATTERN] ...-                  Combines  --and  --not.   See  also  options  --and,  --not, and+                  Combines --and  --not.   See  also  options  --and,  --not,  and                   --bool.             -B NUM, --before-context=NUM-                  Print NUM  lines  of  leading  context  before  matching  lines.+                  Print  NUM  lines  of  leading  context  before  matching lines.                   Places a --group-separator between contiguous groups of matches.                   See also options -A, -C, and -y.             -b, --byte-offset-                  The offset in bytes of a matched line is displayed in  front  of-                  the  respective  matched line.  If -u is specified, displays the-                  offset for each pattern matched on the same line.  Byte  offsets-                  are  exact  for  ASCII, UTF-8, and raw binary input.  Otherwise,+                  The  offset  in bytes of a matched line is displayed in front of+                  the respective matched line.  If -u is specified,  displays  the+                  offset  for each pattern matched on the same line.  Byte offsets+                  are exact for ASCII, UTF-8, and raw  binary  input.   Otherwise,                   the byte offset in the UTF-8 normalized input is displayed.             --binary-files=TYPE-                  Controls searching  and  reporting  pattern  matches  in  binary-                  files.   TYPE  can  be `binary', `without-match`, `text`, `hex`,-                  and `with-hex'.  The default is `binary' to search binary  files-                  and  to  report  a  match  without displaying the match.  `with--                  out-match' ignores binary matches.   `text'  treats  all  binary-                  files  as  text, which might output binary garbage to the termi--                  nal, which can have problematic  consequences  if  the  terminal-                  driver  interprets  some  of  it as commands.  `hex' reports all-                  matches in hexadecimal.  `with-hex' only reports binary  matches-                  in  hexadecimal, leaving text matches alone.  A match is consid--                  ered binary when matching a zero byte  or  invalid  UTF.   Short+                  Controls  searching  and  reporting  pattern  matches  in binary+                  files.  TYPE can be `binary',  `without-match`,  `text`,  `hex`,+                  and  `with-hex'.  The default is `binary' to search binary files+                  and to report a match  without  displaying  the  match.   `with-+                  out-match'  ignores  binary  matches.   `text' treats all binary+                  files as text, which might output binary garbage to  the  termi-+                  nal,  which  can  have  problematic consequences if the terminal+                  driver interprets some of it as  commands.   `hex'  reports  all+                  matches  in hexadecimal.  `with-hex' only reports binary matches+                  in hexadecimal, leaving text matches alone.  A match is  consid-+                  ered  binary  when  matching  a zero byte or invalid UTF.  Short                   options are -a, -I, -U, -W, and -X.             --bool, -%-                  Specifies  Boolean search patterns.  A Boolean search pattern is-                  composed of `AND', `OR', `NOT' operators and grouping  with  `('-                  `)'.   Spacing  between subpatterns is the same as `AND', `|' is-                  the same as `OR', and a `-' is the  same  as  `NOT'.   The  `OR'-                  operator  binds  more  tightly  than `AND'.  For example, --bool-                  'A|B C|D' matches lines with (`A' or  `B')  and  (`C'  or  `D'),-                  --bool  'A  -B'  matches  lines with `A' and not `B'.  Operators-                  `AND', `OR', `NOT' require proper spacing.  For example,  --bool-                  'A  OR B AND C OR D' matches lines with (`A' or `B') and (`C' or-                  `D'), --bool 'A AND NOT B' matches lines with `A'  without  `B'.-                  Quoted  subpatterns are matched literally as strings.  For exam--                  ple, --bool 'A "AND"|"OR"'  matches  lines  with  `A'  and  also-                  either  `AND'  or `OR'.  Parenthesis are used for grouping.  For-                  example, --bool '(A B)|C' matches lines with  `A'  and  `B',  or-                  lines  with  `C'.  Note that all subpatterns in a Boolean search-                  pattern are regular  expressions,  unless  option  -F  is  used.-                  Options  -E,  -F,  -G, -P, and -Z can be combined with --bool to-                  match subpatterns as strings or regular expressions (-E  is  the-                  default.)   This  option  does  not  apply  to -f FILE patterns.-                  Option --stats displays the search patterns applied.   See  also-                  options --and, --andnot, and --not.+                  Specifies Boolean query patterns.  A Boolean  query  pattern  is+                  composed  of  `AND', `OR', `NOT' operators and grouping with `('+                  `)'.  Spacing between subpatterns is the same as `AND',  `|'  is+                  the  same  as  `OR',  and  a `-' is the same as `NOT'.  The `OR'+                  operator binds more tightly than  `AND'.   For  example,  --bool+                  'A|B  C|D'  matches  lines  with  (`A' or `B') and (`C' or `D'),+                  --bool 'A -B' matches lines with `A'  and  not  `B'.   Operators+                  `AND',  `OR', `NOT' require proper spacing.  For example, --bool+                  'A OR B AND C OR D' matches lines with (`A' or `B') and (`C'  or+                  `D'),  --bool  'A AND NOT B' matches lines with `A' without `B'.+                  Quoted subpatterns are matched literally as strings.  For  exam-+                  ple,  --bool  'A  "AND"|"OR"'  matches  lines  with `A' and also+                  either `AND' or `OR'.  Parenthesis are used for  grouping.   For+                  example,  --bool  '(A  B)|C'  matches lines with `A' and `B', or+                  lines with `C'.  Note that all subpatterns in  a  Boolean  query+                  pattern  are  regular  expressions,  unless  option  -F is used.+                  Options -E, -F, -G, -P, and -Z can be combined  with  --bool  to+                  match  subpatterns  as strings or regular expressions (-E is the+                  default.)  This option does  not  apply  to  -f  FILE  patterns.+                  Option  --stats  displays the search patterns applied.  See also+                  options --and, --andnot, --not, --files, and --lines.             --break                   Adds a line break between results from different files.             -C NUM, --context=NUM                   Print NUM lines of leading and trailing context surrounding each-                  match.  Places a --group-separator between contiguous groups  of+                  match.   Places a --group-separator between contiguous groups of                   matches.  See also options -A, -B, and -y.             -c, --count-                  Only  a  count  of selected lines is written to standard output.+                  Only a count of selected lines is written  to  standard  output.                   If -o or -u is specified, counts the number of patterns matched.                   If -v is specified, counts the number of non-matching lines.             --color[=WHEN], --colour[=WHEN]-                  Mark  up  the  matching  text  with the expression stored in the-                  GREP_COLOR or GREP_COLORS environment  variable.   WHEN  can  be+                  Mark up the matching text with  the  expression  stored  in  the+                  GREP_COLOR  or  GREP_COLORS  environment  variable.  WHEN can be                   `never', `always', or `auto', where `auto' marks up matches only                   when output on a terminal.  The default is `auto'.             --colors=COLORS, --colours=COLORS                   Use COLORS to mark up text.  COLORS is a colon-separated list of-                  one  or  more  parameters  `sl=' (selected line), `cx=' (context-                  line), `mt='  (matched  text),  `ms='  (match  selected),  `mc='-                  (match  context),  `fn=' (file name), `ln=' (line number), `cn='+                  one or more parameters `sl='  (selected  line),  `cx='  (context+                  line),  `mt='  (matched  text),  `ms='  (match  selected), `mc='+                  (match context), `fn=' (file name), `ln=' (line  number),  `cn='                   (column number), `bn=' (byte offset), `se=' (separator).  Param--                  eter  values are ANSI SGR color codes or `k' (black), `r' (red),-                  `g' (green),  `y'  (yellow),  `b'  (blue),  `m'  (magenta),  `c'+                  eter values are ANSI SGR color codes or `k' (black), `r'  (red),+                  `g'  (green),  `y'  (yellow),  `b'  (blue),  `m'  (magenta), `c'                   (cyan), `w' (white).  Upper case specifies background colors.  A-                  `+' qualifies a color as bright.  A foreground and a  background-                  color  may  be  combined  with font properties `n' (normal), `f'+                  `+'  qualifies a color as bright.  A foreground and a background+                  color may be combined with font  properties  `n'  (normal),  `f'                   (faint), `h' (highlight), `i' (invert), `u' (underline).  Param-                   eter `hl' enables file name hyperlinks.  Parameter `rv' reverses-                  the `sl=' and `cx='  parameters  with  option  -v.   Selectively+                  the  `sl='  and  `cx='  parameters  with option -v.  Selectively                   overrides GREP_COLORS.             --config[=FILE], ---[FILE]-                  Use  configuration  FILE.   The  default  FILE is `.ugrep'.  The-                  working directory is checked  first  for  FILE,  then  the  home-                  directory.   The options specified in the configuration FILE are+                  Use configuration FILE.  The  default  FILE  is  `.ugrep'.   The+                  working  directory  is  checked  first  for  FILE, then the home+                  directory.  The options specified in the configuration FILE  are                   parsed first, followed by the remaining options specified on the                   command line. @@ -3671,146 +3697,146 @@ in markdown:             --cpp  Output file matches in C++.  See also options --format and -u. -           --csv  Output  file matches in CSV.  If -H, -n, -k, or -b is specified,+           --csv  Output file matches in CSV.  If -H, -n, -k, or -b is  specified,                   additional values are output.  See also options --format and -u.             -D ACTION, --devices=ACTION-                  If  an  input  file  is  a device, FIFO or socket, use ACTION to-                  process it.  By default, ACTION  is  `skip',  which  means  that+                  If an input file is a device, FIFO  or  socket,  use  ACTION  to+                  process  it.   By  default,  ACTION  is `skip', which means that                   devices are silently skipped.  If ACTION is `read', devices read                   just as if they were ordinary files.             -d ACTION, --directories=ACTION-                  If an input file is a directory, use ACTION to process  it.   By-                  default,  ACTION  is  `skip',  i.e.,  silently  skip directories+                  If  an  input file is a directory, use ACTION to process it.  By+                  default, ACTION  is  `skip',  i.e.,  silently  skip  directories                   unless specified on the command line.  If ACTION is `read', warn-                  when  directories  are  read  as input.  If ACTION is `recurse',+                  when directories are read as input.   If  ACTION  is  `recurse',                   read all files under each directory, recursively, following sym--                  bolic  links  only  if  they  are  on the command line.  This is-                  equivalent  to  the  -r  option.    If   ACTION   is   `derefer-+                  bolic links only if they are  on  the  command  line.   This  is+                  equivalent   to   the   -r   option.   If  ACTION  is  `derefer-                   ence-recurse', read all files under each directory, recursively,-                  following symbolic links.  This is equivalent to the -R  option.+                  following  symbolic links.  This is equivalent to the -R option.             --depth=[MIN,][MAX], -1, -2 ... -9, --10, --11 ...-                  Restrict  recursive  searches  from  MIN to MAX directory levels-                  deep, where -1 (--depth=1) searches the specified  path  without-                  recursing  into  subdirectories.   Note that -3 -5, -3-5, or -35-                  searches 3 to 5 levels deep.  Enables -R if  -R  or  -r  is  not+                  Restrict recursive searches from MIN  to  MAX  directory  levels+                  deep,  where  -1 (--depth=1) searches the specified path without+                  recursing into subdirectories.  Note that -3 -5,  -3-5,  or  -35+                  searches  3  to  5  levels  deep.  Enables -R if -R or -r is not                   specified.             --dotall-                  Dot  `.' in regular expressions matches anything, including new--                  line.  Note that `.*' matches all input and should not be  used.+                  Dot `.' in regular expressions matches anything, including  new-+                  line.   Note that `.*' matches all input and should not be used.             -E, --extended-regexp-                  Interpret  patterns as extended regular expressions (EREs). This+                  Interpret patterns as extended regular expressions (EREs).  This                   is the default.             -e PATTERN, --regexp=PATTERN-                  Specify a PATTERN used during the search of the input: an  input-                  line  is  selected  if it matches any of the specified patterns.+                  Specify  a PATTERN used during the search of the input: an input+                  line is selected if it matches any of  the  specified  patterns.                   Note that longer patterns take precedence over shorter patterns.-                  This  option is most useful when multiple -e options are used to-                  specify multiple patterns, when a pattern  begins  with  a  dash-                  (`-'),  to  specify  a pattern after option -f or after the FILE+                  This option is most useful when multiple -e options are used  to+                  specify  multiple  patterns,  when  a pattern begins with a dash+                  (`-'), to specify a pattern after option -f or  after  the  FILE                   arguments.             --encoding=ENCODING-                  The encoding  format  of  the  input,  where  ENCODING  can  be:-                  `binary',  `ASCII',  `UTF-8',  `UTF-16', `UTF-16BE', `UTF-16LE',-                  `UTF-32',  `UTF-32BE',   `UTF-32LE',   `LATIN1',   `ISO-8859-1',+                  The  encoding  format  of  the  input,  where  ENCODING  can be:+                  `binary', `ASCII', `UTF-8',  `UTF-16',  `UTF-16BE',  `UTF-16LE',+                  `UTF-32',   `UTF-32BE',   `UTF-32LE',   `LATIN1',  `ISO-8859-1',                   `ISO-8859-2',    `ISO-8859-3',    `ISO-8859-4',    `ISO-8859-5',                   `ISO-8859-6',    `ISO-8859-7',    `ISO-8859-8',    `ISO-8859-9',-                  `ISO-8859-10',   `ISO-8859-11',   `ISO-8859-13',  `ISO-8859-14',-                  `ISO-8859-15',  `ISO-8859-16',  `MAC',   `MACROMAN',   `EBCDIC',-                  `CP437',   `CP850',   `CP858',   `CP1250',  `CP1251',  `CP1252',+                  `ISO-8859-10',  `ISO-8859-11',   `ISO-8859-13',   `ISO-8859-14',+                  `ISO-8859-15',   `ISO-8859-16',   `MAC',  `MACROMAN',  `EBCDIC',+                  `CP437',  `CP850',  `CP858',   `CP1250',   `CP1251',   `CP1252',                   `CP1253',  `CP1254',  `CP1255',  `CP1256',  `CP1257',  `CP1258',                   `KOI8-R', `KOI8-U', `KOI8-RU'.             --exclude=GLOB                   Skip files whose name matches GLOB using wildcard matching, same                   as -g ^GLOB.  GLOB can use **, *, ?, and [...] as wildcards, and-                  \  to  quote  a wildcard or backslash character literally.  When-                  GLOB contains a `/',  full  pathnames  are  matched.   Otherwise-                  basenames  are  matched.  When GLOB ends with a `/', directories-                  are excluded as if --exclude-dir is specified.  Otherwise  files-                  are  excluded.   Note that --exclude patterns take priority over-                  --include patterns.  GLOB should  be  quoted  to  prevent  shell+                  \ to quote a wildcard or backslash  character  literally.   When+                  GLOB  contains  a  `/',  full  pathnames are matched.  Otherwise+                  basenames are matched.  When GLOB ends with a  `/',  directories+                  are  excluded as if --exclude-dir is specified.  Otherwise files+                  are excluded.  Note that --exclude patterns take  priority  over+                  --include  patterns.   GLOB  should  be  quoted to prevent shell                   globbing.  This option may be repeated.             --exclude-dir=GLOB-                  Exclude  directories  whose  name  matches  GLOB  from recursive-                  searches, same as -g ^GLOB/.  GLOB can use **, *, ?,  and  [...]-                  as  wildcards,  and \ to quote a wildcard or backslash character+                  Exclude directories  whose  name  matches  GLOB  from  recursive+                  searches,  same  as -g ^GLOB/.  GLOB can use **, *, ?, and [...]+                  as wildcards, and \ to quote a wildcard or  backslash  character                   literally.   When  GLOB  contains  a  `/',  full  pathnames  are-                  matched.    Otherwise   basenames   are   matched.    Note  that-                  --exclude-dir patterns take  priority  over  --include-dir  pat--                  terns.   GLOB  should be quoted to prevent shell globbing.  This+                  matched.   Otherwise   basenames   are   matched.    Note   that+                  --exclude-dir  patterns  take  priority  over --include-dir pat-+                  terns.  GLOB should be quoted to prevent shell  globbing.   This                   option may be repeated.             --exclude-from=FILE-                  Read the globs from FILE and skip files  and  directories  whose-                  name  matches  one  or more globs.  A glob can use **, *, ?, and+                  Read  the  globs  from FILE and skip files and directories whose+                  name matches one or more globs.  A glob can use **,  *,  ?,  and                   [...] as wildcards, and \ to quote a wildcard or backslash char-                   acter literally.  When a glob contains a `/', full pathnames are-                  matched.  Otherwise basenames are matched.   When  a  glob  ends-                  with  a  `/',  directories  are  excluded as if --exclude-dir is-                  specified.  Otherwise files are excluded.  A glob starting  with-                  a  `!'  overrides  previously-specified  exclusions by including-                  matching files.  Lines starting with a `#' and  empty  lines  in-                  FILE  are  ignored.  When FILE is a `-', standard input is read.+                  matched.   Otherwise  basenames  are  matched.  When a glob ends+                  with a `/', directories are  excluded  as  if  --exclude-dir  is+                  specified.   Otherwise files are excluded.  A glob starting with+                  a `!' overrides  previously-specified  exclusions  by  including+                  matching  files.   Lines  starting with a `#' and empty lines in+                  FILE are ignored.  When FILE is a `-', standard input  is  read.                   This option may be repeated.             --exclude-fs=MOUNTS-                  Exclude  file  systems  specified  by  MOUNTS   from   recursive-                  searches,  MOUNTS  is  a comma-separated list of mount points or-                  pathnames  of  directories   on   file   systems.    Note   that-                  --exclude-fs  mounts  take  priority  over  --include-fs mounts.+                  Exclude   file   systems  specified  by  MOUNTS  from  recursive+                  searches, MOUNTS is a comma-separated list of  mount  points  or+                  pathnames   of   directories   on   file   systems.   Note  that+                  --exclude-fs mounts  take  priority  over  --include-fs  mounts.                   This option may be repeated.             -F, --fixed-strings-                  Interpret pattern as a set of fixed strings, separated  by  new--                  lines,  any  of which is to be matched.  This makes ugrep behave-                  as fgrep.  If a PATTERN is specified, or -e PATTERN or  -N  PAT--                  TERN,  then  this  option  has  no effect on -f FILE patterns to-                  allow -f FILE patterns to narrow or widen the scope of the  PAT-+                  Interpret  pattern  as a set of fixed strings, separated by new-+                  lines, any of which is to be matched.  This makes  ugrep  behave+                  as  fgrep.   If a PATTERN is specified, or -e PATTERN or -N PAT-+                  TERN, then this option has no effect  on  -f  FILE  patterns  to+                  allow  -f FILE patterns to narrow or widen the scope of the PAT-                   TERN search.             -f FILE, --file=FILE-                  Read  newline-separated patterns from FILE.  White space in pat-+                  Read newline-separated patterns from FILE.  White space in  pat-                   terns is significant.  Empty lines in FILE are ignored.  If FILE-                  does  not  exist,  the GREP_PATH environment variable is used as-                  path  to   FILE.    If   that   fails,   looks   for   FILE   in-                  /usr/local/share/ugrep/patterns.   When  FILE is a `-', standard+                  does not exist, the GREP_PATH environment variable  is  used  as+                  path   to   FILE.    If   that   fails,   looks   for   FILE  in+                  /usr/local/share/ugrep/patterns.  When FILE is a  `-',  standard                   input is read.  Empty files contain no patterns; thus nothing is                   matched.  This option may be repeated.             --filter=COMMANDS                   Filter files through the specified COMMANDS first before search-                   ing.   COMMANDS  is  a  comma-separated  list  of  `exts:command-                  [option  ...]',  where `exts' is a comma-separated list of file--                  name extensions and `command' is a filter utility.   The  filter-                  utility  should  read  from standard input and write to standard+                  [option ...]', where `exts' is a comma-separated list  of  file-+                  name  extensions  and `command' is a filter utility.  The filter+                  utility should read from standard input and  write  to  standard                   output.  Files matching one of `exts' are filtered.  When `exts'                   is `*', files with non-matching extensions are filtered.  One or-                  more `option' separated by spacing may be specified,  which  are-                  passed  verbatim to the command.  A `%' as `option' expands into-                  the pathname to search.  For example, --filter='pdf:pdftotext  %-                  -'  searches PDF files.  The `%' expands into a `-' when search--                  ing standard input.  Option --label=.ext may be used to  specify+                  more  `option'  separated by spacing may be specified, which are+                  passed verbatim to the command.  A `%' as `option' expands  into+                  the  pathname to search.  For example, --filter='pdf:pdftotext %+                  -' searches PDF files.  The `%' expands into a `-' when  search-+                  ing  standard input.  Option --label=.ext may be used to specify                   extension `ext' when searching standard input.             --filter-magic-label=[+]LABEL:MAGIC-                  Associate  LABEL  with files whose signature "magic bytes" match-                  the MAGIC regex pattern.   Only  files  that  have  no  filename-                  extension  are  labeled, unless +LABEL is specified.  When LABEL-                  matches an extension specified in --filter=COMMANDS, the  corre-+                  Associate LABEL with files whose signature "magic  bytes"  match+                  the  MAGIC  regex  pattern.   Only  files  that have no filename+                  extension are labeled, unless +LABEL is specified.   When  LABEL+                  matches  an extension specified in --filter=COMMANDS, the corre-                   sponding command is invoked.  This option may be repeated.             --format=FORMAT-                  Output    FORMAT-formatted    matches.    For   example   --for--                  mat='%f:%n:%O%~' outputs matching lines `%O' with filename  `%f`-                  and  line  number  `%n'  followed  by  a  newline `%~'.  Context+                  Output   FORMAT-formatted   matches.    For    example    --for-+                  mat='%f:%n:%O%~'  outputs matching lines `%O' with filename `%f`+                  and line number  `%n'  followed  by  a  newline  `%~'.   Context                   options -A, -B, -C, and -y are ignored.  See `man ugrep' section                   FORMAT. @@ -3822,15 +3848,15 @@ in markdown:                   behave as traditional grep.             -g GLOBS, --glob=GLOBS-                  Search only files whose name matches the  specified  comma-sepa-+                  Search  only  files whose name matches the specified comma-sepa-                   rated list of GLOBS, same as --include='glob' for each `glob' in-                  GLOBS.  When a `glob' is preceded by a `!' or a `^', skip  files-                  whose  name  matches  `glob',  same  as  --exclude='glob'.  When-                  `glob' contains a `/', full pathnames  are  matched.   Otherwise+                  GLOBS.   When a `glob' is preceded by a `!' or a `^', skip files+                  whose name  matches  `glob',  same  as  --exclude='glob'.   When+                  `glob'  contains  a  `/', full pathnames are matched.  Otherwise                   basenames are matched.  When `glob' ends with a `/', directories-                  are    matched,     same     as     --include-dir='glob'     and-                  --exclude-dir='glob'.   A leading `/' matches the working direc--                  tory.  This option may be repeated  and  may  be  combined  with+                  are     matched,     same     as     --include-dir='glob'    and+                  --exclude-dir='glob'.  A leading `/' matches the working  direc-+                  tory.   This  option  may  be  repeated and may be combined with                   options -M, -O and -t to expand the recursive search.             --group-separator[=SEP]@@ -3838,12 +3864,12 @@ in markdown:                   The default is a double hyphen (`--').             -H, --with-filename-                  Always print the  filename  with  output  lines.   This  is  the+                  Always  print  the  filename  with  output  lines.   This is the                   default when there is more than one file to search.             -h, --no-filename-                  Never  print  filenames  with output lines.  This is the default-                  when there is only one file (or only standard input) to  search.+                  Never print filenames with output lines.  This  is  the  default+                  when  there is only one file (or only standard input) to search.             --heading, -+                   Group matches per file.  Adds a heading and a line break between@@ -3853,94 +3879,94 @@ in markdown:                   Display a help message, specifically on WHAT when specified.             --hexdump=[1-8][a][b][c][h]-                  Output matches in 1 to 8 columns of 8 hexadecimal  octets.   The-                  default  is 2 columns or 16 octets per line.  Option `a' outputs-                  a `*' for all hex lines that are identical to the  previous  hex-                  line,  `b'  removes  all space breaks, `c' removes the character-                  column and `h' removes hex spacing.  Enables -X if -W or  -X  is+                  Output  matches  in 1 to 8 columns of 8 hexadecimal octets.  The+                  default is 2 columns or 16 octets per line.  Option `a'  outputs+                  a  `*'  for all hex lines that are identical to the previous hex+                  line, `b' removes all space breaks, `c'  removes  the  character+                  column  and  `h' removes hex spacing.  Enables -X if -W or -X is                   not specified.             --hidden, -.                   Search hidden files and directories.             --hyperlink-                  Hyperlinks  are  enabled for file names when colors are enabled.+                  Hyperlinks are enabled for file names when colors  are  enabled.                   Same as --colors=hl.             -I, --ignore-binary-                  Ignore matches in binary files.  This option  is  equivalent  to+                  Ignore  matches  in  binary files.  This option is equivalent to                   the --binary-files=without-match option.             -i, --ignore-case-                  Perform  case  insensitive  matching.  By default, ugrep is case-                  sensitive.  By default, this option  applies  to  ASCII  letters+                  Perform case insensitive matching.  By default,  ugrep  is  case+                  sensitive.   By  default,  this  option applies to ASCII letters                   only.  Use options -P and -i for Unicode case insensitive match-                   ing.             --ignore-files[=FILE]-                  Ignore files and directories matching the  globs  in  each  FILE-                  that  is encountered in recursive searches.  The default FILE is-                  `.gitignore'.  Matching files and  directories  located  in  the-                  directory  of  a  FILE's  location  and in directories below are-                  ignored   by   temporarily   overriding   the   --exclude    and-                  --exclude-dir   globs,  as  if  --exclude-from=FILE  is  locally-                  enforced.  Globbing is the same as --exclude-from=FILE and  sup--                  ports  gitignore  syntax,  but directories are not automatically+                  Ignore  files  and  directories  matching the globs in each FILE+                  that is encountered in recursive searches.  The default FILE  is+                  `.gitignore'.   Matching  files  and  directories located in the+                  directory of a FILE's location  and  in  directories  below  are+                  ignored    by   temporarily   overriding   the   --exclude   and+                  --exclude-dir  globs,  as  if  --exclude-from=FILE  is   locally+                  enforced.   Globbing is the same as --exclude-from=FILE and sup-+                  ports gitignore syntax, but directories  are  not  automatically                   excluded from searches (use a glob ending with a `/' to identify-                  directories  to  ignore,  same  as  git).  Files and directories-                  explicitly  specified  as  command  line  arguments  are   never+                  directories to ignore, same  as  git).   Files  and  directories+                  explicitly   specified  as  command  line  arguments  are  never                   ignored.  This option may be repeated.             --include=GLOB-                  Search  only files whose name matches GLOB using wildcard match-+                  Search only files whose name matches GLOB using wildcard  match-                   ing, same as -g GLOB.  GLOB can use **, *, ?, and [...] as wild--                  cards,  and  \ to quote a wildcard or backslash character liter--                  ally.  When GLOB contains a `/',  full  pathnames  are  matched.-                  Otherwise  basenames  are  matched.   When GLOB ends with a `/',+                  cards, and \ to quote a wildcard or backslash  character  liter-+                  ally.   When  GLOB  contains  a `/', full pathnames are matched.+                  Otherwise basenames are matched.  When GLOB  ends  with  a  `/',                   directories are included as if --include-dir is specified.  Oth--                  erwise  files  are  included.  Note that --exclude patterns take+                  erwise files are included.  Note that  --exclude  patterns  take                   priority over --include patterns.  GLOB should be quoted to pre-                   vent shell globbing.  This option may be repeated.             --include-dir=GLOB-                  Only  directories whose name matches GLOB are included in recur--                  sive searches, same as -g GLOB/.  GLOB can use  **,  *,  ?,  and+                  Only directories whose name matches GLOB are included in  recur-+                  sive  searches,  same  as  -g GLOB/.  GLOB can use **, *, ?, and                   [...] as wildcards, and \ to quote a wildcard or backslash char--                  acter literally.  When GLOB contains a `/', full  pathnames  are-                  matched.    Otherwise   basenames   are   matched.    Note  that-                  --exclude-dir patterns take  priority  over  --include-dir  pat--                  terns.   GLOB  should be quoted to prevent shell globbing.  This+                  acter  literally.   When GLOB contains a `/', full pathnames are+                  matched.   Otherwise   basenames   are   matched.    Note   that+                  --exclude-dir  patterns  take  priority  over --include-dir pat-+                  terns.  GLOB should be quoted to prevent shell  globbing.   This                   option may be repeated.             --include-from=FILE-                  Read the globs from FILE and search only files  and  directories-                  whose  name matches one or more globs.  A glob can use **, *, ?,-                  and [...] as wildcards, and \ to quote a wildcard  or  backslash+                  Read  the  globs from FILE and search only files and directories+                  whose name matches one or more globs.  A glob can use **, *,  ?,+                  and  [...]  as wildcards, and \ to quote a wildcard or backslash                   character literally.  When a glob contains a `/', full pathnames                   are matched.  Otherwise basenames are matched.  When a glob ends-                  with  a  `/',  directories  are  included as if --include-dir is-                  specified.  Otherwise files are included.  A glob starting  with-                  a  `!'  overrides  previously-specified  inclusions by excluding-                  matching files.  Lines starting with a `#' and  empty  lines  in-                  FILE  are  ignored.  When FILE is a `-', standard input is read.+                  with a `/', directories are  included  as  if  --include-dir  is+                  specified.   Otherwise files are included.  A glob starting with+                  a `!' overrides  previously-specified  inclusions  by  excluding+                  matching  files.   Lines  starting with a `#' and empty lines in+                  FILE are ignored.  When FILE is a `-', standard input  is  read.                   This option may be repeated.             --include-fs=MOUNTS-                  Only file systems specified by MOUNTS are included in  recursive-                  searches.   MOUNTS  is a comma-separated list of mount points or-                  pathnames  of  directories  on  file  systems.    --include-fs=.-                  restricts  recursive  searches to the file system of the working-                  directory only.  Note that  --exclude-fs  mounts  take  priority+                  Only  file systems specified by MOUNTS are included in recursive+                  searches.  MOUNTS is a comma-separated list of mount  points  or+                  pathnames   of  directories  on  file  systems.   --include-fs=.+                  restricts recursive searches to the file system of  the  working+                  directory  only.   Note  that  --exclude-fs mounts take priority                   over --include-fs mounts.  This option may be repeated.             -J NUM, --jobs=NUM-                  Specifies  the  number  of  threads spawned to search files.  By-                  default an optimum number of threads is spawned to search  files-                  simultaneously.   -J1  disables threading: files are searched in+                  Specifies the number of threads spawned  to  search  files.   By+                  default  an optimum number of threads is spawned to search files+                  simultaneously.  -J1 disables threading: files are  searched  in                   the same order as specified.             -j, --smart-case-                  Perform case insensitive matching like option -i, unless a  pat-+                  Perform  case insensitive matching like option -i, unless a pat-                   tern is specified with a literal ASCII upper case letter.             --json Output file matches in JSON.  If -H, -n, -k, or -b is specified,@@ -3950,40 +3976,44 @@ in markdown:                   Start searching at line FIRST, stop at line LAST when specified.             -k, --column-number-                  The column number of a matched pattern is displayed in front  of-                  the  respective  matched  line,  starting at column 1.  Tabs are+                  The  column number of a matched pattern is displayed in front of+                  the respective matched line, starting at  column  1.   Tabs  are                   expanded when columns are counted, see also option --tabs.             -L, --files-without-match-                  Only the names of files not containing selected lines are  writ--                  ten  to  standard  output.   Pathnames  are listed once per file+                  Only  the names of files not containing selected lines are writ-+                  ten to standard output.  Pathnames  are  listed  once  per  file                   searched.   If  the  standard  input  is  searched,  the  string                   ``(standard input)'' is written.             -l, --files-with-matches                   Only the names of files containing selected lines are written to-                  standard output.  ugrep will only search a file  until  a  match-                  has  been  found,  making  searches  potentially less expensive.-                  Pathnames are listed once per file searched.   If  the  standard+                  standard  output.   ugrep  will only search a file until a match+                  has been found,  making  searches  potentially  less  expensive.+                  Pathnames  are  listed  once per file searched.  If the standard                   input is searched, the string ``(standard input)'' is written.             --label=LABEL-                  Displays  the LABEL value when input is read from standard input-                  where a file name would normally be printed in the output.   As-+                  Displays the LABEL value when input is read from standard  input+                  where  a file name would normally be printed in the output.  As-                   sociates a filename extension with standard input when LABEL has                   a suffix.  The default value is `(standard input)'.             --line-buffered                   Force output to be line buffered instead of block buffered. +           --lines+                  Apply  Boolean  queries to match lines, the opposite of --files.+                  This is the default Boolean query mode to match specific  lines.+            -M MAGIC, --file-magic=MAGIC-                  Only files matching the signature pattern  MAGIC  are  searched.-                  The  signature "magic bytes" at the start of a file are compared-                  to the MAGIC regex pattern.  When matching,  the  file  will  be-                  searched.   When MAGIC is preceded by a `!' or a `^', skip files+                  Only  files  matching  the signature pattern MAGIC are searched.+                  The signature "magic bytes" at the start of a file are  compared+                  to  the  MAGIC  regex  pattern.  When matching, the file will be+                  searched.  When MAGIC is preceded by a `!' or a `^', skip  files                   with matching MAGIC signatures.  This option may be repeated and-                  may  be  combined  with  options -O and -t to expand the search.-                  Every file on the search path is read,  making  searches  poten-+                  may be combined with options -O and -t  to  expand  the  search.+                  Every  file  on  the search path is read, making searches poten-                   tially more expensive.             -m NUM, --max-count=NUM@@ -3993,63 +4023,73 @@ in markdown:                   Match all input.  Same as specifying an empty pattern to search.             --max-files=NUM-                  Restrict the number of files matched to NUM.  Note  that  --sort-                  or  -J1  may  be  specified  to  produce replicable results.  If+                  Restrict  the  number of files matched to NUM.  Note that --sort+                  or -J1 may be  specified  to  produce  replicable  results.   If                   --sort is specified, the number of threads spawned is limited to                   NUM.             --mmap[=MAX]-                  Use  memory  maps  to search files.  By default, memory maps are-                  used under certain conditions to improve performance.  When  MAX+                  Use memory maps to search files.  By default,  memory  maps  are+                  used  under certain conditions to improve performance.  When MAX                   is specified, use up to MAX mmap memory per thread.             -N PATTERN, --neg-regexp=PATTERN-                  Specify  a negative PATTERN used during the search of the input:-                  an input line is selected only if it matches any of  the  speci--                  fied  patterns  unless  a  subpattern  of  PATTERN.   Same as -e-                  (?^PATTERN).  Negative PATTERN matches are  essentially  removed-                  before  any  other  patterns are matched.  Note that longer pat-+                  Specify a negative PATTERN used during the search of the  input:+                  an  input  line is selected only if it matches any of the speci-+                  fied patterns unless  a  subpattern  of  PATTERN.   Same  as  -e+                  (?^PATTERN).   Negative  PATTERN matches are essentially removed+                  before any other patterns are matched.  Note  that  longer  pat-                   terns take precedence over shorter patterns.  This option may be                   repeated.             -n, --line-number-                  Each  output line is preceded by its relative line number in the-                  file, starting at line 1.  The line number counter is reset  for+                  Each output line is preceded by its relative line number in  the+                  file,  starting at line 1.  The line number counter is reset for                   each file processed.             --no-group-separator-                  Removes  the  group  separator  line from the output for context+                  Removes the group separator line from  the  output  for  context                   options -A, -B, and -C.             --not [-e] PATTERN-                  Specifies that PATTERN should not match.  Note that -e  A  --not-                  -e  B  matches  lines with `A' or lines without a `B'.  To match-                  lines with `A' that have no `B', specify -e  A  --andnot  -e  B.-                  Option  --stats  displays the search patterns applied.  See also-                  options --and, --andnot, and --bool.+                  Specifies  that  PATTERN should not match.  Note that -e A --not+                  -e B matches lines with `A' or lines without a  `B'.   To  match+                  lines  with  `A'  that  have no `B', specify -e A --andnot -e B.+                  Option --stats displays the search patterns applied.   See  also+                  options --and, --andnot, --bool, --files, and --lines.             -O EXTENSIONS, --file-extension=EXTENSIONS-                  Search only files whose filename extensions match the  specified-                  comma-separated  list  of  EXTENSIONS, same as --include='*.ext'-                  for each `ext' in EXTENSIONS.  When an `ext' is  preceded  by  a-                  `!'  or  a  `^',  skip  files  whose filename extensions matches-                  `ext', same as --exclude='*.ext'.  This option may  be  repeated-                  and  may  be  combined  with options -g, -M and -t to expand the+                  Search  only files whose filename extensions match the specified+                  comma-separated list of EXTENSIONS,  same  as  --include='*.ext'+                  for  each  `ext'  in EXTENSIONS.  When an `ext' is preceded by a+                  `!' or a `^',  skip  files  whose  filename  extensions  matches+                  `ext',  same  as --exclude='*.ext'.  This option may be repeated+                  and may be combined with options -g, -M and  -t  to  expand  the                   recursive search.             -o, --only-matching-                  Print only the matching part  of  lines.   When  multiple  lines-                  match,  the  line numbers with option -n are displayed using `|'-                  as the field separator for each additional line matched  by  the-                  pattern.   If  -u is specified, ungroups multiple matches on the-                  same line.  This option cannot be combined with options -A,  -B,+                  Print  only  the  matching  part  of lines.  When multiple lines+                  match, the line numbers with option -n are displayed  using  `|'+                  as  the  field separator for each additional line matched by the+                  pattern.  If -u is specified, ungroups multiple matches  on  the+                  same  line.  This option cannot be combined with options -A, -B,                   -C, -v, and -y.             --only-line-number                   The line number of the matching line in the file is output with--                  out displaying the match.  The line number counter is reset  for+                  out  displaying the match.  The line number counter is reset for                   each file processed. +           --files+                  Apply Boolean queries to match files, the opposite  of  --lines.+                  A  file  matches  if all Boolean conditions are satisfied by the+                  lines matched in the file.  For example, --files -e A --and -e B+                  -e  C  --andnot  -e D matches a file if some lines match `A' and+                  some lines match (`B' or `C') and no line in  the  file  matches+                  `D'.   May  also  be  specified  as  --files  --bool 'A B|C -D'.+                  Option -v cannot be specified with --files.   See  also  options+                  --and, --andnot, --not, --bool and --lines.+            -P, --perl-regexp                   Interpret  PATTERN  as  a  Perl  regular expression using PCRE2.                   Note that Perl pattern matching differs from  the  default  grep@@ -4811,7 +4851,7 @@ in markdown:   -    ugrep 3.3.12                   December 11, 2021                      UGREP(1)+    ugrep 3.4.0                    December 20, 2021                      UGREP(1)  � [Back to table of contents](#toc) 

bin/win32/ugrep.exe

Binary file changed

bin/win64/ugrep.exe

Binary file changed

configure

@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles.-# Generated by GNU Autoconf 2.71 for ugrep 3.3.+# Generated by GNU Autoconf 2.71 for ugrep 3.4. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,@@ -609,8 +609,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='ugrep' PACKAGE_TARNAME='ugrep'-PACKAGE_VERSION='3.3'-PACKAGE_STRING='ugrep 3.3'+PACKAGE_VERSION='3.4'+PACKAGE_STRING='ugrep 3.4' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1349,7 +1349,7 @@ if test "$ac_init_help" = "long"; then   # Omit some internal or obsolete options to make the list less imposing.   # This message is too long to be a string in the A/UX 3.1 sh.   cat <<_ACEOF-\`configure' configures ugrep 3.3 to adapt to many kinds of systems.+\`configure' configures ugrep 3.4 to adapt to many kinds of systems.  Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1420,7 +1420,7 @@ fi  if test -n "$ac_init_help"; then   case $ac_init_help in-     short | recursive ) echo "Configuration of ugrep 3.3:";;+     short | recursive ) echo "Configuration of ugrep 3.4:";;    esac   cat <<\_ACEOF @@ -1562,7 +1562,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then   cat <<\_ACEOF-ugrep configure 3.3+ugrep configure 3.4 generated by GNU Autoconf 2.71  Copyright (C) 2021 Free Software Foundation, Inc.@@ -2099,7 +2099,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by ugrep $as_me 3.3, which was+It was created by ugrep $as_me 3.4, which was generated by GNU Autoconf 2.71.  Invocation command line was    $ $0$ac_configure_args_raw@@ -3586,7 +3586,7 @@ fi  # Define the identity of the package.  PACKAGE='ugrep'- VERSION='3.3'+ VERSION='3.4'   printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h@@ -9358,7 +9358,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log="-This file was extended by ugrep $as_me 3.3, which was+This file was extended by ugrep $as_me 3.4, which was generated by GNU Autoconf 2.71.  Invocation command line was    CONFIG_FILES    = $CONFIG_FILES@@ -9426,7 +9426,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\-ugrep config.status 3.3+ugrep config.status 3.4 configured by $0, generated by GNU Autoconf 2.71,   with options \\"\$ac_cs_config\\" 

configure.ac

@@ -1,4 +1,4 @@-AC_INIT([ugrep],[3.3])+AC_INIT([ugrep],[3.4]) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_HEADERS([config.h]) AC_COPYRIGHT([Copyright (C) 2019-2021 Robert van Engelen, Genivia Inc.])

man/ugrep.1

@@ -1,4 +1,4 @@-.TH UGREP "1" "December 11, 2021" "ugrep 3.3.12" "User Commands"+.TH UGREP "1" "December 20, 2021" "ugrep 3.4.0" "User Commands" .SH NAME \fBugrep\fR, \fBug\fR -- file pattern searcher .SH SYNOPSIS@@ -67,7 +67,7 @@ pattern.  For example, \fB\-e\fR \fIA\fR \fB\-e\fR \fIB\fR \fB\-\-and\fR \fB\-e\ (`A' or `B') and (`C' or `D').  Note that multiple \fB\-e\fR \fIPATTERN\fR are alternations that bind more tightly together than \fB\-\-and\fR.  Option \fB\-\-stats\fR displays the search patterns applied.  See also options-\fB\-\-not\fR, \fB\-\-andnot\fR, and \fB\-\-bool\fR.+\fB\-\-not\fR, \fB\-\-andnot\fR, \fB\-\-bool\fR, \fB\-\-files\fR, and \fB\-\-lines\fR. .TP \fB\-\-andnot\fR [[\fB\-e\fR] PATTERN] ... Combines \fB\-\-and\fR \fB\-\-not\fR.  See also options \fB\-\-and\fR, \fB\-\-not\fR, and \fB\-\-bool\fR.@@ -98,7 +98,7 @@ matches alone.  A match is considered binary when matching a zero byte or invalid UTF.  Short options are \fB\-a\fR, \fB\-I\fR, \fB\-U\fR, \fB\-W\fR, and \fB\-X\fR. .TP \fB\-\-bool\fR, \fB\-%\fR-Specifies Boolean search patterns.  A Boolean search pattern is+Specifies Boolean query patterns.  A Boolean query pattern is composed of `AND', `OR', `NOT' operators and grouping with `(' `)'. Spacing between subpatterns is the same as `AND', `|' is the same as `OR', and a `\-' is the same as `NOT'.  The `OR' operator binds@@ -112,12 +112,12 @@ literally as strings.  For example, \fB\-\-bool\fR 'A "AND"|"OR"' matches lines with `A' and also either `AND' or `OR'.  Parenthesis are used for grouping.  For example, \fB\-\-bool\fR '(A B)|C' matches lines with `A' and `B', or lines with `C'.  Note that all subpatterns in a Boolean-search pattern are regular expressions, unless option \fB\-F\fR is used.+query pattern are regular expressions, unless option \fB\-F\fR is used. Options \fB\-E\fR, \fB\-F\fR, \fB\-G\fR, \fB\-P\fR, and \fB\-Z\fR can be combined with \fB\-\-bool\fR to match subpatterns as strings or regular expressions (\fB\-E\fR is the default.) This option does not apply to \fB\-f\fR \fIFILE\fR patterns.  Option \fB\-\-stats\fR displays the search patterns applied.  See also options \fB\-\-and\fR,-\fB\-\-andnot\fR, and \fB\-\-not\fR.+\fB\-\-andnot\fR, \fB\-\-not\fR, \fB\-\-files\fR, and \fB\-\-lines\fR. .TP \fB\-\-break\fR Adds a line break between results from different files.@@ -461,6 +461,10 @@ a suffix.  The default value is `(standard input)'. \fB\-\-line\-buffered\fR Force output to be line buffered instead of block buffered. .TP+\fB\-\-lines\fR+Apply Boolean queries to match lines, the opposite of \fB\-\-files\fR.+This is the default Boolean query mode to match specific lines.+.TP \fB\-M\fR \fIMAGIC\fR, \fB\-\-file\-magic\fR=\fIMAGIC\fR Only files matching the signature pattern MAGIC are searched.  The signature "magic bytes" at the start of a file are compared to@@ -508,7 +512,7 @@ Specifies that PATTERN should not match.  Note that \fB\-e\fR \fIA\fR \fB\-\-not matches lines with `A' or lines without a `B'.  To match lines with `A' that have no `B', specify \fB\-e\fR \fIA\fR \fB\-\-andnot\fR \fB\-e\fR \fIB\fR.  Option \fB\-\-stats\fR displays the search patterns applied.  See also options \fB\-\-and\fR,-\fB\-\-andnot\fR, and \fB\-\-bool\fR.+\fB\-\-andnot\fR, \fB\-\-bool\fR, \fB\-\-files\fR, and \fB\-\-lines\fR. .TP \fB\-O\fR \fIEXTENSIONS\fR, \fB\-\-file\-extension\fR=\fIEXTENSIONS\fR Search only files whose filename extensions match the specified@@ -530,6 +534,16 @@ The line number of the matching line in the file is output without displaying the match.  The line number counter is reset for each file processed. .TP+\fB\-\-files\fR+Apply Boolean queries to match files, the opposite of \fB\-\-lines\fR.  A+file matches if all Boolean conditions are satisfied by the lines+matched in the file.  For example, \fB\-\-files\fR \fB\-e\fR \fIA\fR \fB\-\-and\fR \fB\-e\fR \fIB\fR \fB\-e\fR \fIC\fR+\fB\-\-andnot\fR \fB\-e\fR \fID\fR matches a file if some lines match `A' and some lines+match (`B' or `C') and no line in the file matches `D'.  May also+be specified as \fB\-\-files\fR \fB\-\-bool\fR 'A B|C \fB\-D\fR'.  Option \fB\-v\fR cannot be+specified with \fB\-\-files\fR.  See also options \fB\-\-and\fR, \fB\-\-andnot\fR, \fB\-\-not\fR,+\fB\-\-bool\fR and \fB\-\-lines\fR.+.TP \fB\-P\fR, \fB\-\-perl\-regexp\fR Interpret PATTERN as a Perl regular expression using PCRE2. Note that Perl pattern matching differs from the default grep POSIX

src/cnf.cpp

@@ -285,7 +285,7 @@ void CNF::OpTree::normalize(bool invert)       else if (op == OR)         op = AND;     }-    +     // P&(Q&R) => P&Q&R     // P|(Q|R) => P|Q|R     auto i = list.begin();@@ -534,7 +534,7 @@ void CNF::new_pattern(PATTERN mask, const char *pattern)   } } -// prune empty OR-terms and OR-terms with empty patterns that match anything+// prune empty OR terms and OR terms with empty patterns that match anything void CNF::prune() {   // -x: empty patterns match empty lines@@ -547,7 +547,7 @@ void CNF::prune()    while (i != e)   {-    // erase empty term and erase NULL term without NOT-OR terms, unless the first term and -f FILE is specified+    // erase empty term and erase NULL term without OR NOT terms, unless the first term and -f FILE is specified     if ((i->empty() || (i->size() == 1 && (!i->front() || i->front()->empty()))) && (i != s || flag_file.empty()))       terms.erase(i++);     else@@ -597,10 +597,12 @@ void CNF::report(FILE *output) const   if (empty())     return; +  fprintf(output, flag_files ? "Files " : "Lines ");+   if (flag_fuzzy > 0)-    fprintf(output, "Lines fuzzy-matched with max edit distance %zu if:" NEWLINESTR "  ", flag_fuzzy & 255);+    fprintf(output, "fuzzy-matched with max edit distance %zu if:" NEWLINESTR "  ", flag_fuzzy & 255);   else-    fprintf(output, "Lines matched if:" NEWLINESTR "  ");+    fprintf(output, "matched if:" NEWLINESTR "  ");    if (!flag_file.empty())   {@@ -614,11 +616,13 @@ void CNF::report(FILE *output) const       if (or_sep)         fprintf(output, " or "); -      fprintf(output, "%s", filename.c_str()); +      fprintf(output, "%s", filename.c_str());        or_sep = true;     }     fprintf(output, " matches");+    if (flag_files)+      fprintf(output, " a line");      // if the first CNF term is left empty then we match -f FILE with additional constraints, i.e. not as an alternation     if (terms.front().empty())@@ -647,9 +651,17 @@ void CNF::report(FILE *output) const         else           fprintf(output, "\"%s\"", (*j)->c_str());         if (j != i->begin())+        {           fprintf(output, " does not match");+          if (flag_files)+            fprintf(output, " any line");+        }         else+        {           fprintf(output, " matches");+          if (flag_files)+            fprintf(output, " a line");+        }          or_sep = true;         and_sep = true;@@ -660,43 +672,52 @@ void CNF::report(FILE *output) const   fprintf(output, NEWLINESTR); } -// return all OR-terms of the CNF joined together+// return all OR terms of the CNF joined together std::string CNF::adjoin() const {   std::string adjoined;+  const char *sep = flag_basic_regexp ? "\\|" : "|"; -  bool allnot = true;--  for (const auto& i : terms)+  if (flag_files)   {-    if (i.size() <= 1)-    {-      allnot = false;-      break;-    }+    // --files: join all OR and OR NOT terms+    for (const auto& i : terms)+      for (const auto& j : i)+        if (j && !j->empty())+          adjoined.append(*j).append(sep);   }--  if (!allnot)+  else   {-    const char *sep = flag_basic_regexp ? "\\|" : "|";+    bool allnot = true;      for (const auto& i : terms)-      if (i.front() && !i.front()->empty())-        adjoined.append(*i.front()).append(sep);--    if (!adjoined.empty())     {-      // pop unused ending '|' (or BRE '\|')-      adjoined.pop_back();-      if (flag_basic_regexp)-        adjoined.pop_back();+      if (i.size() <= 1)+      {+        allnot = false;+        break;+      }     }++    // --lines: join all OR terms unless all of them are paired with OR NOT terms+    if (!allnot)+      for (const auto& i : terms)+        if (i.front() && !i.front()->empty())+          adjoined.append(*i.front()).append(sep);+  }++  if (!adjoined.empty())+  {+    // pop unused ending '|' (or BRE '\|')+    adjoined.pop_back();+    if (flag_basic_regexp)+      adjoined.pop_back();   }    return adjoined; } -// return the first OR-terms of the CNF+// return the first OR terms of the CNF std::string CNF::first() const {   if (!terms.empty() && terms.front().front())

src/flag.hpp

@@ -79,6 +79,7 @@ extern bool flag_cpp; extern bool flag_csv; extern bool flag_decompress; extern bool flag_dereference;+extern bool flag_files; extern bool flag_files_with_matches; extern bool flag_files_without_match; extern bool flag_fixed_strings;

src/output.cpp

@@ -363,6 +363,8 @@ void Output::header(const char *& pathname, const std::string& partname, size_t // output "Binary file ... matches" void Output::binary_file_matches(const char *pathname, const std::string& partname) {+  if ((mode_ & BINARY) != 0)+    return;   str(color_off);   str("Binary file");   str(color_fn);@@ -394,6 +396,7 @@ void Output::binary_file_matches(const char *pathname, const std::string& partna   str(color_off);   str(" matches");   nl();+  mode_ |= BINARY; }  // get a group capture's string pointer and size specified by %[ARG] as arg, if any

src/output.hpp

@@ -57,6 +57,9 @@ class Output {    static constexpr size_t SIZE = 16384;          // size of each buffer in the buffers container   static constexpr size_t STOP = UNDEFINED_SIZE; // if last == STOP, cancel output+  static constexpr int FLUSH  = 1;               // mode bit: flush each line of output+  static constexpr int HOLD   = 2;               // mode bit: hold output+  static constexpr int BINARY = 4;               // mode bit: binary file found    struct Buffer { char data[SIZE]; }; // data buffer in the buffers container @@ -304,7 +307,7 @@ class Output {       lock_(NULL),       slot_(0),       lineno_(0),-      flush_(flag_line_buffered)+      mode_(flag_line_buffered ? FLUSH : 0)   {     grow();   }@@ -507,7 +510,7 @@ class Output { #ifdef OS_WIN       chr('\r'); #else-      ;+      { } #endif     chr('\n');     check_flush();@@ -516,16 +519,38 @@ class Output {   // enable line buffered mode to flush each line to output   void set_flush()   {-    flush_ = true;+    mode_ |= FLUSH;   } -  // flush if output is line buffered+  // flush if output is line buffered to flush each line   void check_flush()   {-    if (flush_)+    if (mode_ == FLUSH)       flush();   } +  // hold the output and do not flush, buffer all output until further notice+  void hold()+  {+    mode_ |= HOLD;+  }++  // launch output when held back+  void launch()+  {+    if ((mode_ & HOLD) != 0)+    {+      mode_ &= ~HOLD;+      check_flush();+    }+  }++  // return true when holding the output+  bool holding()+  {+    return mode_ & HOLD;+  }+   // synchronize output by threads on the given sync object   void sync_on(Sync *s)   {@@ -548,7 +573,7 @@ class Output {       sync->acquire(lock_, slot_);   } -  // flush the buffers and acquire lock+  // acquire lock and flush the buffers, if not held back   void flush()   {     if (buf_ != buffers_.begin() || cur_ > buf_->data)@@ -592,10 +617,22 @@ class Output {     }   } +  // discard buffered output+  void discard()+  {+    buf_ = buffers_.begin();+    cur_ = buf_->data;+  }+   // flush output and release sync slot, if one was assigned with sync_on()   void release()   {-    flush();+    if ((mode_ & HOLD) == 0)+      flush();+    else+      discard();++    mode_ = flag_line_buffered ? FLUSH : 0;      if (sync != NULL)       sync->release(lock_);@@ -655,7 +692,7 @@ class Output {   // next buffer, allocate one if needed (when multi-threaded and lock is owned by another thread)   void next()   {-    if (sync == NULL || sync->try_acquire(lock_))+    if ((mode_ & HOLD) == 0 && (sync == NULL || sync->try_acquire(lock_)))     {       flush();     }@@ -694,7 +731,7 @@ class Output {   Buffers                       buffers_; // buffers container   Buffers::iterator             buf_;     // current buffer in the container   char                         *cur_;     // current position in the current buffer-  bool                          flush_;   // true if output is line buffered+  int                           mode_;    // bitmask 1 if line buffered 2 if hold  }; 

src/stats.cpp

@@ -63,6 +63,14 @@ void Stats::report(FILE *output)   fprintf(output, "The following pathname selections and restrictions were applied:" NEWLINESTR);   if (flag_config != NULL)     fprintf(output, "  --config=%s" NEWLINESTR, flag_config_file.c_str());+  if (flag_min_depth > 0 && flag_max_depth > 0)+    fprintf(output, "  --depth=%zu,%zu" NEWLINESTR, flag_min_depth, flag_max_depth);+  else if (flag_min_depth > 0)+    fprintf(output, "  --depth=%zu," NEWLINESTR, flag_min_depth);+  else if (flag_max_depth > 0)+    fprintf(output, "  --depth=%zu" NEWLINESTR, flag_max_depth);+  if (flag_files)+    fprintf(output, "  --files" NEWLINESTR); #ifdef WITH_HIDDEN   if (flag_hidden)     fprintf(output, "  --hidden (default)" NEWLINESTR);@@ -74,12 +82,6 @@ void Stats::report(FILE *output)   else     fprintf(output, "  --no-hidden (default)" NEWLINESTR); #endif-  if (flag_min_depth > 0 && flag_max_depth > 0)-    fprintf(output, "  --depth=%zu,%zu" NEWLINESTR, flag_min_depth, flag_max_depth);-  else if (flag_min_depth > 0)-    fprintf(output, "  --depth=%zu," NEWLINESTR, flag_min_depth);-  else if (flag_max_depth > 0)-    fprintf(output, "  --depth=%zu" NEWLINESTR, flag_max_depth);   for (auto& i : flag_ignore_files)     fprintf(output, "  --ignore-files='%s'" NEWLINESTR, i.c_str());   for (auto& i : ignore)

src/stats.hpp

@@ -90,19 +90,21 @@ class Stats {   // atomically update the number of matching files found, excluding files in archives returns true if max file matches (+ number of threads-1 when sorting) is not reached yet   static bool found_file()   {-    if (flag_max_files > 0)-      return fileno.fetch_add(1, std::memory_order_relaxed) < flag_max_files;-    fileno.fetch_add(1, std::memory_order_relaxed);-    return true;+    size_t n = fileno.fetch_add(1, std::memory_order_relaxed);+    return flag_max_files > 0 ? n < flag_max_files : true;   }    // atomically update the number of matching files found, including files in archives, returns true if max file matches (+ number of threads-1 when sorting) is not reached yet   static bool found_part()   {-    if (flag_max_files > 0)-      return partno.fetch_add(1, std::memory_order_relaxed) < flag_max_files;-    partno.fetch_add(1, std::memory_order_relaxed);-    return true;+    size_t n = partno.fetch_add(1, std::memory_order_relaxed);+    return flag_max_files > 0 ? n < flag_max_files : true;+  }++  // undo found_part+  static void undo_found_part()+  {+    partno.fetch_sub(1, std::memory_order_relaxed);   }    // the number of matching files found, excluding files in archives

src/ugrep.cpp

@@ -317,6 +317,7 @@ bool flag_cpp                      = false; bool flag_csv                      = false; bool flag_decompress               = false; bool flag_dereference              = false;+bool flag_files                    = false; bool flag_files_with_matches       = false; bool flag_files_without_match      = false; bool flag_fixed_strings            = false;@@ -648,6 +649,9 @@ struct Grep {   // CNF of AND/OR/NOT matchers   typedef std::list<std::list<std::unique_ptr<reflex::AbstractMatcher>>> Matchers; +  // exit search exception+  struct EXIT_SEARCH : public std::exception { };+   // entry type   enum class Type { SKIP, DIRECTORY, OTHER }; @@ -1050,11 +1054,14 @@ struct Grep {         if (flag_max_line > 0 && lineno > flag_max_line)           break; -        // --max-files: max reached?-        if (flag_invert_match && matches == 0 && !Stats::found_part())+        if (matches == 0 && flag_invert_match)         {-          stop = true;-          break;+          // --max-files: max reached?+          if (!Stats::found_part())+          {+            stop = true;+            break;+          }         }          // -m: max number of matches reached?@@ -1268,11 +1275,14 @@ struct Grep {         if (flag_max_line > 0 && lineno > flag_max_line)           break; -        // --max-files: max reached?-        if (flag_invert_match && matches == 0 && !Stats::found_part())+        if (matches == 0 && flag_invert_match)         {-          stop = true;-          break;+          // --max-files: max reached?+          if (!Stats::found_part())+          {+            stop = true;+            break;+          }         }          // -m: max number of matches reached?@@ -1581,11 +1591,14 @@ struct Grep {         if (flag_max_line > 0 && lineno > flag_max_line)           break; -        // --max-files: max reached?-        if (matches == 0 && !Stats::found_part())+        if (matches == 0)         {-          stop = true;-          break;+          // --max-files: max reached?+          if (!Stats::found_part())+          {+            stop = true;+            break;+          }         }          // -m: max number of matches reached?@@ -1723,33 +1736,158 @@ struct Grep {   virtual void search(const char *pathname);    // check CNF AND/OR/NOT conditions are met for the line(s) spanning bol to eol-  bool cnf_matching(const char *bol, const char *eol)+  bool cnf_matching(const char *bol, const char *eol, bool acquire = false)   {-    // for each AND term-    for (const auto& i : *matchers)+    if (flag_files)     {-      auto j = i.begin();-      auto e = i.end();+      if (out.holding())+      {+        size_t k = 0;    // iterate over matching[] bitmask+        bool all = true; // all terms matched++        // for each AND term check if the AND term was matched before or has a match this time+        for (const auto& i : *matchers)+        {+          // an OR term hasn't matched before+          if (!matching[k])+          {+            auto j = i.begin();+            auto e = i.end();++            if (j != e)+            {+              // check OR terms+              if (*j && (*j)->buffer(const_cast<char*>(bol), eol - bol + 1).find() != 0)+              {+                matching[k] = true;+                ++j;+              }+              else+              {+                // check OR NOT terms+                size_t l = 0;     // iterate over notmaching[k] bitmask+                bool none = true; // all not-terms matched++                while (++j != e)+                {+                  if (*j && !notmatching[k][l])+                  {+                    if ((*j)->buffer(const_cast<char*>(bol), eol - bol + 1).find() != 0)+                      notmatching[k][l] = true;+                    else+                      all = none = false;+                  }++                  ++l;+                }++                if (none)+                {+                  // when all not-terms matched and we don't have a positive alternative then stop searching this file+                  if (!*i.begin())+                    throw EXIT_SEARCH();++                  all = false;+                }+              }+            }+          }+          ++k;+        } -      if (j != e)+        // if all terms matched globally per file then remove the hold to launch output+        if (all)+        {+          if (acquire)+            out.acquire();++          // --max-files: max reached?+          if (!Stats::found_part())+            throw EXIT_SEARCH();++          out.launch();+        }+      }+    }+    else+    {+      // for each AND term check if the line has a match+      for (const auto& i : *matchers)       {-        // check OR terms-        if (*j && (*j)->buffer(const_cast<char*>(bol), eol - bol + 1).find() != 0)-          continue;+        auto j = i.begin();+        auto e = i.end(); -        // check OR NOT terms-        while (++j != e)-          if (*j && (*j)->buffer(const_cast<char*>(bol), eol - bol + 1).find() == 0)-            break;+        if (j != e)+        {+          // check OR terms+          if (*j && (*j)->buffer(const_cast<char*>(bol), eol - bol + 1).find() != 0)+            continue; -        if (j == e)-          return false;+          // check OR NOT terms+          while (++j != e)+            if (*j && (*j)->buffer(const_cast<char*>(bol), eol - bol + 1).find() == 0)+              break;++          if (j == e)+            return false;+        }       }     }      return true;   } +  // if CNF AND/OR/NOT conditions are met globally then launch output after searching a file with --files+  bool cnf_satisfied(bool acquire = false)+  {+    if (out.holding())+    {+      size_t k = 0; // iterate over matching[] bitmask++      // for each AND term check if the term was matched before+      for (const auto& i : *matchers)+      {+        // an OR term hasn't matched+        if (!matching[k])+        {+          // return if there are no OR NOT terms to check+          if (i.size() <= 1)+            return false;++          auto j = i.begin();+          auto e = i.end();++          // check if not all of the OR NOT terms matched+          if (j != e)+          {+            size_t l = 0; // iterate over notmaching[k] bitmask+            while (++j != e)+            {+              if (*j && !notmatching[k][l])+                break;+              ++l;+            }+            // return if all OR NOT terms matched+            if (j == e)+              return false;+          }+        }+        ++k;+      }++      if (acquire)+        out.acquire();++      // --max-files: max reached?+      if (!Stats::found_part())+        throw EXIT_SEARCH();++      out.launch();+    }++    return true;+  }+   // open a file for (binary) reading and assign input, decompress the file when -z, --decompress specified, may throw bad_alloc   bool open_file(const char *pathname)   {@@ -2930,38 +3068,38 @@ struct Grep {   // after opening a file with init_read, check if it is binary   bool init_is_binary()   {-    // limit checking to first buffer filled with input up to 4K, which should suffice, to improve performance+    // limit checking to first buffer filled with input up to 16K, which should suffice, to improve performance     size_t avail = matcher->avail();-    if (avail > 4096)-      avail = 4096;-    return is_binary(matcher->begin(), avail);-  }--  const char              *filename;      // the name of the file being searched-  std::string              partname;      // the name of an extracted file from an archive-  std::string              restline;      // a buffer to store the rest of a line to search-  Output                   out;           // asynchronous output-  reflex::AbstractMatcher *matcher;       // the pattern matcher we're using, never NULL-  Matchers                *matchers;      // the CNF of AND/OR/NOT matchers or NULL-  MMap                     mmap;          // mmap state-  reflex::Input            input;         // input to the matcher-  FILE                    *file;          // the current input file+    return is_binary(matcher->begin(), avail < 16384 ? avail : 16384);+  }++  const char                    *filename;      // the name of the file being searched+  std::string                    partname;      // the name of an extracted file from an archive+  std::string                    restline;      // a buffer to store the rest of a line to search+  Output                         out;           // asynchronous output+  reflex::AbstractMatcher       *matcher;       // the pattern matcher we're using, never NULL+  Matchers                      *matchers;      // the CNF of AND/OR/NOT matchers or NULL+  std::vector<bool>              matching;      // bitmap to keep track of globally matching CNF terms+  std::vector<std::vector<bool>> notmatching;   // bitmap to keep track of globally matching OR NOT CNF terms+  MMap                           mmap;          // mmap state+  reflex::Input                  input;         // input to the matcher+  FILE                          *file;          // the current input file #ifndef OS_WIN-  StdInHandler             stdin_handler; // a handler to handle non-blocking stdin from a TTY or a slow pipe+  StdInHandler                   stdin_handler; // a handler to handle non-blocking stdin from a TTY or a slow pipe #endif #ifdef HAVE_LIBZ-  zstreambuf              *zstream;       // the decompressed stream from the current input file-  std::istream            *stream;        // input stream layered on the decompressed stream+  zstreambuf                    *zstream;       // the decompressed stream from the current input file+  std::istream                  *stream;        // input stream layered on the decompressed stream #ifdef WITH_DECOMPRESSION_THREAD-  std::thread              thread;        // decompression thread-  std::atomic_bool         thread_end;    // true if decompression thread should terminate-  int                      pipe_fd[2];    // decompressed stream pipe-  std::mutex               pipe_mutex;    // mutex to extract files in thread-  std::condition_variable  pipe_zstrm;    // cv to control new pipe creation-  std::condition_variable  pipe_ready;    // cv to control new pipe creation-  std::condition_variable  pipe_close;    // cv to control new pipe creation-  volatile bool            extracting;    // true if extracting files from TAR or ZIP archive-  volatile bool            waiting;       // true if decompression thread is waiting+  std::thread                    thread;        // decompression thread+  std::atomic_bool               thread_end;    // true if decompression thread should terminate+  int                            pipe_fd[2];    // decompressed stream pipe+  std::mutex                     pipe_mutex;    // mutex to extract files in thread+  std::condition_variable        pipe_zstrm;    // cv to control new pipe creation+  std::condition_variable        pipe_ready;    // cv to control new pipe creation+  std::condition_variable        pipe_close;    // cv to control new pipe creation+  volatile bool                  extracting;    // true if extracting files from TAR or ZIP archive+  volatile bool                  waiting;       // true if decompression thread is waiting #endif #endif @@ -3980,6 +4118,8 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a                   flag_file_magic.emplace_back(arg + 11);                 else if (strncmp(arg, "file-type=", 10) == 0)                   flag_file_type.emplace_back(arg + 10);+                else if (strcmp(arg, "files") == 0)+                  flag_files = true;                 else if (strcmp(arg, "files-with-matches") == 0)                   flag_files_with_matches = true;                 else if (strcmp(arg, "files-without-match") == 0)@@ -4019,7 +4159,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a                     strcmp(arg, "format-open") == 0)                   usage("missing argument for --", arg);                 else-                  usage("invalid option --", arg, "--file, --file-extension, --file-magic, --file-type, --files-with-matches, --files-without-match, --fixed-strings, --filter, --filter-magic-label, --format, --format-begin, --format-close, --format-end, --format-open, --fuzzy or --free-space");+                  usage("invalid option --", arg, "--file, --file-extension, --file-magic, --file-type, --files, --files-with-matches, --files-without-match, --fixed-strings, --filter, --filter-magic-label, --format, --format-begin, --format-close, --format-end, --format-open, --fuzzy or --free-space");                 break;                case 'g':@@ -4104,8 +4244,10 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a                   flag_line_number = true;                 else if (strcmp(arg, "line-regexp") == 0)                   flag_line_regexp = true;+                else if (strcmp(arg, "lines") == 0)+                  flag_files = false;                 else-                  usage("invalid option --", arg, "--label, --line-buffered, --line-number or --line-regexp");+                  usage("invalid option --", arg, "--label, --line-buffered, --line-number, --line-regexp or --lines");                 break;                case 'm':@@ -5717,7 +5859,11 @@ void terminal()         // --pager: if output is to a TTY then page through the results          // open a pipe to a forked pager+#ifdef OS_WIN+        output = popen(flag_pager, "wb");+#else         output = popen(flag_pager, "w");+#endif         if (output == NULL)           error("cannot open pipe to pager", flag_pager); @@ -6110,7 +6256,7 @@ void ugrep()     // prune empty terms from the CNF that match anything     bcnf.prune(); -    // split the patterns at newlines, standard grep+    // split the patterns at newlines, standard grep behavior     bcnf.split();      if (flag_file.empty())@@ -6151,6 +6297,13 @@ void ugrep()     }   } +  // -v with --files is not permitted+  if (flag_files && flag_invert_match)+  {+    abort("-v is not permitted with --files, invert the Boolean query instead");+    flag_invert_match = false;+  }+   // -x or --match: enable -Y and disable --dotall and -w   if (flag_line_regexp || flag_match)   {@@ -6365,6 +6518,13 @@ void ugrep()     // -q overrides -l and -L     flag_files_with_matches = false;     flag_files_without_match = false;++    // disable --format options+    flag_format_begin = NULL;+    flag_format_open = NULL;+    flag_format = NULL;+    flag_format_close = NULL;+    flag_format_end = NULL;   }    // -L: enable -l and flip -v i.e. -L=-lv and -l=-Lv@@ -6452,7 +6612,7 @@ void ugrep()     matcher_options.append("T=").push_back(static_cast<char>(flag_tabs) + '0');    // --format-begin-  if (!flag_quiet && flag_format_begin != NULL)+  if (flag_format_begin != NULL)     format(flag_format_begin, 0);    size_t nodes = 0;@@ -6712,7 +6872,7 @@ void ugrep()   }    // --format-end-  if (!flag_quiet && flag_format_end != NULL)+  if (flag_format_end != NULL)     format(flag_format_end, Stats::found_parts());    // --stats: display stats when we're done@@ -7657,6 +7817,28 @@ void Grep::search(const char *pathname)     {       size_t matches = 0; +      // --files: reset the matching[] bitmask used in cnf_matching() for each matcher in matchers+      if (flag_files && matchers != NULL)+      {+        // hold the output+        out.hold();++        // reset the bit corresponding to each matcher in matchers+        size_t n = matchers->size();+        matching.resize(0);+        matching.resize(n);++        // reset the bit corresponding to the OR NOT terms of each matcher in matchers+        notmatching.resize(n);+        size_t j = 0;+        for (auto& i : *matchers)+        {+          notmatching[j].resize(0);+          notmatching[j].resize(i.size() > 0 ? i.size() - 1 : 0);+          ++j;+        }+      }+       if (flag_quiet || flag_files_with_matches)       {         // option -q, -l, or -L@@ -7664,24 +7846,37 @@ void Grep::search(const char *pathname)         if (!init_read())           goto exit_search; -        while (true)-        {-          matches = matcher->find() != 0;+        // --format: whether to out.acquire() early before Stats::found_part()+        bool acquire = flag_format != NULL && (flag_format_open != NULL || flag_format_close != NULL); -          if (matches == 0 || matchers == NULL)+        while (matcher->find())+        {+          // --range: max line exceeded?+          if (flag_max_line > 0 && matcher->lineno() > flag_max_line)             break; -          const char *eol = matcher->eol(true); // warning: call eol() before bol() and end()-          const char *bol = matcher->bol();+          if (matchers != NULL)+          {+            const char *eol = matcher->eol(true); // warning: call eol() before bol() and end()+            const char *bol = matcher->bol(); -          // check CNF AND/OR/NOT matching-          if (cnf_matching(bol, eol))-            break;+            // check CNF AND/OR/NOT matching+            if (!cnf_matching(bol, eol, acquire) || out.holding())+              continue;+          }++          matches = 1;+          break;         } -        // --range: max line exceeded?-        if (flag_max_line > 0 && matcher->lineno() > flag_max_line)-          matches = 0;+        // --files: if we are still holding the output and CNF is finally satisfyable then a match was made+        if (flag_files && matchers != NULL)+        {+          if (!cnf_satisfied(acquire))+            goto exit_search;++          matches = 1;+        }          // -v: invert         if (flag_invert_match)@@ -7690,12 +7885,15 @@ void Grep::search(const char *pathname)         if (matches > 0)         {           // --format-open or format-close: we must acquire lock early before Stats::found_part()-          if (flag_files_with_matches && (flag_format_open != NULL || flag_format_close != NULL))+          if (acquire)             out.acquire(); -          // --max-files: max reached?-          if (!Stats::found_part())-            goto exit_search;+          if (!flag_files || matchers == NULL)+          {+            // --max-files: max reached?+            if (!Stats::found_part())+              goto exit_search;+          }            // -l or -L           if (flag_files_with_matches)@@ -7744,16 +7942,53 @@ void Grep::search(const char *pathname)       {         // option -c -        if (init_read())+        if (!init_read())+          goto exit_search;++        // --format: whether to out.acquire() early before Stats::found_part()+        bool acquire = flag_format != NULL && (flag_format_open != NULL || flag_format_close != NULL);++        if (flag_ungroup || flag_only_matching)         {-          if (flag_ungroup || flag_only_matching)+          // -co or -cu: count the number of patterns matched in the file++          while (matcher->find())           {-            // -co or -cu: count the number of patterns matched in the file+            // --range: max line exceeded?+            if (flag_max_line > 0 && matcher->lineno() > flag_max_line)+              break; -            while (matcher->find())+            if (matchers != NULL)+            {+              const char *eol = matcher->eol(true); // warning: call eol() before bol() and end()+              const char *bol = matcher->bol();++              // check CNF AND/OR/NOT matching, with --files acquire lock before Stats::found_part()+              if (!cnf_matching(bol, eol, acquire))+                continue;+            }++            ++matches;++            // -m: max number of matches reached?+            if (flag_max_count > 0 && matches >= flag_max_count)+              break;+          }+        }+        else+        {+          // -c without -o/-u: count the number of matching lines++          size_t lineno = 0;++          while (matcher->find())+          {+            size_t current_lineno = matcher->lineno();++            if (lineno != current_lineno)             {               // --range: max line exceeded?-              if (flag_max_line > 0 && matcher->lineno() > flag_max_line)+              if (flag_max_line > 0 && current_lineno > flag_max_line)                 break;                if (matchers != NULL)@@ -7761,8 +7996,8 @@ void Grep::search(const char *pathname)                 const char *eol = matcher->eol(true); // warning: call eol() before bol() and end()                 const char *bol = matcher->bol(); -                // check CNF AND/OR/NOT matching-                if (!cnf_matching(bol, eol))+                // check CNF AND/OR/NOT matching, with --files acquire lock before Stats::found_part()+                if (!cnf_matching(bol, eol, acquire))                   continue;               } @@ -7771,62 +8006,39 @@ void Grep::search(const char *pathname)               // -m: max number of matches reached?               if (flag_max_count > 0 && matches >= flag_max_count)                 break;-            }-          }-          else-          {-            // -c without -o/-u: count the number of matching lines--            size_t lineno = 0;--            while (matcher->find())-            {-              size_t current_lineno = matcher->lineno();--              if (lineno != current_lineno)-              {-                // --range: max line exceeded?-                if (flag_max_line > 0 && current_lineno > flag_max_line)-                  break;--                if (matchers != NULL)-                {-                  const char *eol = matcher->eol(true); // warning: call eol() before bol() and end()-                  const char *bol = matcher->bol();--                  // check CNF AND/OR/NOT matching-                  if (!cnf_matching(bol, eol))-                    continue;-                }--                ++matches; -                // -m: max number of matches reached?-                if (flag_max_count > 0 && matches >= flag_max_count)-                  break;--                lineno = current_lineno;-              }+              lineno = current_lineno;             }+          } -            // -c with -v: count non-matching lines-            if (flag_invert_match)-            {-              matches = matcher->lineno() - matches;-              if (matches > 0)-                --matches;-            }+          // -c with -v: count non-matching lines+          if (flag_invert_match)+          {+            matches = matcher->lineno() - matches;+            if (matches > 0)+              --matches;           }         } -        // --format-open or --format-close: we must acquire lock early before Stats::found_part()-        if (flag_format_open != NULL || flag_format_close != NULL)-          out.acquire();+        // --files: if we are still holding the output and CNF is not satisfyable then no global matches were made+        if (flag_files && matchers != NULL)+        {+          if (!cnf_satisfied(acquire))+            goto exit_search; // we cannot report 0 matches and ensure accurate output+        }+        else+        {+          // --format-open or --format-close: we must acquire lock early before Stats::found_part()+          if (acquire)+            out.acquire(); -        // --max-files: max reached?-        if (matches > 0 || flag_format_open != NULL || flag_format_close != NULL)-          if (!Stats::found_part())-            goto exit_search;+          // --max-files: max reached?+          // unfortunately, allowing 'acquire' below produces "x matching + y in archives"+          // but without this we cannot produce correct format-open and format-close outputs+          if (matches > 0 || acquire)+            if (!Stats::found_part())+              goto exit_search;+        }          if (flag_format != NULL)         {@@ -7884,6 +8096,9 @@ void Grep::search(const char *pathname)         if (!init_read())           goto exit_search; +        // whether to out.acquire() early before Stats::found_part()+        bool acquire = flag_format_open != NULL || flag_format_close != NULL;+         if (flag_invert_match)         {           // FormatInvertMatchHandler requires lineno to be set precisely, i.e. after skipping --range lines@@ -7965,23 +8180,44 @@ void Grep::search(const char *pathname)               const char *bol = matcher->bol();                // check CNF AND/OR/NOT matching-              if (!cnf_matching(bol, eol))+              if (!cnf_matching(bol, eol, acquire))                 continue;             }              // output --format-open             if (matches == 0)             {-              // --format-open or --format-close: we must acquire lock early before Stats::found_part()-              if (flag_format_open != NULL || flag_format_close != NULL)-                out.acquire();+              if (flag_files && matchers != NULL)+              {+                // --format-open: we must acquire lock early before Stats::found_part()+                if (acquire && out.holding())+                {+                  out.acquire(); -              // --max-files: max reached?-              if (!Stats::found_part())-                goto exit_search;+                  // --max-files: max reached?+                  if (!Stats::found_part())+                    goto exit_search;+                }+              }+              else+              {+                // --format-open: we must acquire lock early before Stats::found_part()+                if (acquire)+                  out.acquire();++                // --max-files: max reached?+                if (!Stats::found_part())+                  goto exit_search;+              }                if (flag_format_open != NULL)+              {                 out.format(flag_format_open, pathname, partname, Stats::found_parts(), matcher, false, Stats::found_parts() > 1);++                // --files: undo files count+                if (flag_files && matchers != NULL && out.holding())+                  Stats::undo_found_part();+              }             }              ++matches;@@ -7997,6 +8233,11 @@ void Grep::search(const char *pathname)           }         } +        // --files: if we are still holding the output and CNF is not satisfyable then no global matches were made+        if (flag_files && matchers != NULL)+          if (!cnf_satisfied(true))+            goto exit_search;+         // output --format-close         if (matches > 0 && flag_format_close != NULL)           out.format(flag_format_close, pathname, partname, Stats::found_parts(), matcher, false, Stats::found_parts() > 1);@@ -8033,9 +8274,12 @@ void Grep::search(const char *pathname)                 continue;             } -            // --max-files: max reached?-            if (matches == 0 && !Stats::found_part())-              goto exit_search;+            if (matches == 0 && (!flag_files || matchers == NULL))+            {+              // --max-files: max reached?+              if (!Stats::found_part())+                goto exit_search;+            }              ++matches; @@ -8113,9 +8357,12 @@ void Grep::search(const char *pathname)                 continue;             } -            // --max-files: max reached?-            if (matches == 0 && !Stats::found_part())-              goto exit_search;+            if (matches == 0 && (!flag_files || matchers == NULL))+            {+              // --max-files: max reached?+              if (!Stats::found_part())+                goto exit_search;+            }              if (binfile || (binary && !flag_hex && !flag_with_hex))             {@@ -8129,6 +8376,9 @@ void Grep::search(const char *pathname)                 matches = 1;               } +              if (flag_files && matchers != NULL && out.holding())+                continue;+               goto done_search;             } @@ -8164,6 +8414,9 @@ void Grep::search(const char *pathname)                 matches = 1;               } +              if (flag_files && matchers != NULL && out.holding())+                continue;+               goto done_search;             } @@ -8281,9 +8534,12 @@ void Grep::search(const char *pathname)             if (matchers != NULL && !cnf_matching(bol, eol))               continue; -            // --max-files: max reached?-            if (matches == 0 && !Stats::found_part())-              goto exit_search;+            if (matches == 0 && (!flag_files || matchers == NULL))+            {+              // --max-files: max reached?+              if (!Stats::found_part())+                goto exit_search;+            }              binary = flag_hex || (!flag_text && is_binary(bol, eol - bol)); @@ -8299,6 +8555,9 @@ void Grep::search(const char *pathname)                 matches = 1;               } +              if (flag_files && matchers != NULL && out.holding())+                continue;+               goto done_search;             } @@ -8739,13 +8998,13 @@ void Grep::search(const char *pathname)                   matches = 1;                 } +                if (flag_files && matchers != NULL && out.holding())+                  continue;+                 goto done_search;               }             } -            if (!flag_invert_match)-              ++matches;-             // --range: max line exceeded?             if (flag_max_line > 0 && current_lineno > flag_max_line)               break;@@ -8754,9 +9013,17 @@ void Grep::search(const char *pathname)             if (stop)               goto exit_search; -            // --max-files: max reached?-            if (!flag_invert_match && matches == 0 && !Stats::found_part())-              goto exit_search;+            if (!flag_invert_match)+            {+              if (matches == 0 && (!flag_files || matchers == NULL))+              {+                // --max-files: max reached?+                if (!Stats::found_part())+                  goto exit_search;+              }++              ++matches;+            }              // -m: max number of matches reached?             if (flag_max_count > 0 && matches >= flag_max_count)@@ -8785,6 +9052,9 @@ void Grep::search(const char *pathname)                 matches = 1;               } +              if (flag_files && matchers != NULL && out.holding())+                continue;+               goto done_search;             } @@ -9141,6 +9411,9 @@ void Grep::search(const char *pathname)                 matches = 1;               } +              if (flag_files && matchers != NULL && out.holding())+                continue;+               goto done_search;             } @@ -9154,9 +9427,14 @@ void Grep::search(const char *pathname)             if (stop)               goto exit_search; -            // --max-files: max reached?-            if (!flag_invert_match && matches == 0 && !Stats::found_part())-              goto exit_search;+            if (matches == 0 && (!flag_files || matchers == NULL))+            {+              // --max-files: max reached?+              if (!Stats::found_part())+                goto exit_search;+            }++            ++matches;              // -m: max number of matches reached?             if (flag_max_count > 0 && matches >= flag_max_count)@@ -9180,12 +9458,12 @@ void Grep::search(const char *pathname)                 matches = 1;               } +              if (flag_files && matchers != NULL && out.holding())+                continue;+               goto done_search;             } -            if (!flag_invert_match)-              ++matches;-             size_t border = matcher->border();             size_t first = matcher->first();             const char *begin = matcher->begin();@@ -9564,9 +9842,14 @@ void Grep::search(const char *pathname)             if (stop)               goto exit_search; -            // --max-files: max reached?-            if (!flag_invert_match && matches == 0 && !Stats::found_part())-              goto exit_search;+            /* logically OK but dead code because -v+            if (matches == 0 && !flag_invert_match && (!flag_files || matchers == NULL))+            {+              // --max-files: max reached?+              if (!Stats::found_part())+                goto exit_search;+            }+            */              // -m: max number of matches reached?             if (flag_max_count > 0 && matches >= flag_max_count)@@ -9586,16 +9869,18 @@ void Grep::search(const char *pathname)                 {                   matches = 0;                 }-                else if (flag_invert_match)+                else // if (flag_invert_match) is true                 {                   lineno = last_lineno = current_lineno + matcher->lines() - 1;                   continue;                 }+                /* logically OK but dead code because -v                 else                 {                   out.binary_file_matches(pathname, partname);                   matches = 1;                 }+                */                  goto done_search;               }@@ -9678,16 +9963,18 @@ void Grep::search(const char *pathname)                 {                   matches = 0;                 }-                else if (flag_invert_match)+                else // if (flag_invert_match) is true                 {                   lineno = last_lineno = current_lineno + matcher->lines() - 1;                   continue;                 }+                /* logically OK but dead code because -v                 else                 {                   out.binary_file_matches(pathname, partname);                   matches = 1;                 }+                */                  goto done_search;               }@@ -9870,16 +10157,18 @@ void Grep::search(const char *pathname)                   {                     matches = 0;                   }-                  else if (flag_invert_match)+                  else // if (flag_invert_match) is true                   {                     lineno = last_lineno = current_lineno + matcher->lines() - 1;                     continue;                   }+                  /* logically OK but dead code because -v                   else                   {                     out.binary_file_matches(pathname, partname);                     matches = 1;                   }+                  */                    goto done_search;                 }@@ -9960,6 +10249,11 @@ void Grep::search(const char *pathname)  done_search: +      // --files: check if all CNF conditions are met globally to launch output or reset matches+      if (flag_files && matchers != NULL)+        if (!cnf_satisfied())+          matches = 0;+       // any matches in this file or archive?       if (matches > 0)         matched = true;@@ -9969,6 +10263,11 @@ done_search:         out.nl();     } +    catch (EXIT_SEARCH&)+    {+      // --files: cnf_matching() rejected a file, no need to search this file any further+    }+     catch (...)     {       // this should never happen@@ -10424,7 +10723,7 @@ void help(std::ostream& out)             (`A' or `B') and (`C' or `D').  Note that multiple -e PATTERN are\n\             alternations that bind more tightly together than --and.  Option\n\             --stats displays the search patterns applied.  See also options\n\-            --not, --andnot, and --bool.\n\+            --not, --andnot, --bool, --files, and --lines.\n\     --andnot [[-e] PATTERN] ...\n\             Combines --and --not.  See also options --and, --not, and --bool.\n\     -B NUM, --before-context=NUM\n\@@ -10450,7 +10749,7 @@ void help(std::ostream& out)             matches alone.  A match is considered binary when matching a zero\n\             byte or invalid UTF.  Short options are -a, -I, -U, -W, and -X.\n\     --bool, -%\n\-            Specifies Boolean search patterns.  A Boolean search pattern is\n\+            Specifies Boolean query patterns.  A Boolean query pattern is\n\             composed of `AND', `OR', `NOT' operators and grouping with `(' `)'.\n\             Spacing between subpatterns is the same as `AND', `|' is the same\n\             as `OR', and a `-' is the same as `NOT'.  The `OR' operator binds\n\@@ -10464,12 +10763,12 @@ void help(std::ostream& out)             lines with `A' and also either `AND' or `OR'.  Parenthesis are used\n\             for grouping.  For example, --bool '(A B)|C' matches lines with `A'\n\             and `B', or lines with `C'.  Note that all subpatterns in a Boolean\n\-            search pattern are regular expressions, unless option -F is used.\n\+            query pattern are regular expressions, unless option -F is used.\n\             Options -E, -F, -G, -P, and -Z can be combined with --bool to match\n\             subpatterns as strings or regular expressions (-E is the default.)\n\             This option does not apply to -f FILE patterns.  Option --stats\n\             displays the search patterns applied.  See also options --and,\n\-            --andnot, and --not.\n\+            --andnot, --not, --files, and --lines.\n\     --break\n\             Adds a line break between results from different files.\n\     -C NUM, --context=NUM\n\@@ -10774,6 +11073,9 @@ void help(std::ostream& out)             a suffix.  The default value is `(standard input)'.\n\     --line-buffered\n\             Force output to be line buffered instead of block buffered.\n\+    --lines\n\+            Apply Boolean queries to match lines, the opposite of --files.\n\+            This is the default Boolean query mode to match specific lines.\n\     -M MAGIC, --file-magic=MAGIC\n\             Only files matching the signature pattern MAGIC are searched.  The\n\             signature \"magic bytes\" at the start of a file are compared to\n\@@ -10813,7 +11115,7 @@ void help(std::ostream& out)             matches lines with `A' or lines without a `B'.  To match lines with\n\             `A' that have no `B', specify -e A --andnot -e B.  Option --stats\n\             displays the search patterns applied.  See also options --and,\n\-            --andnot, and --bool.\n\+            --andnot, --bool, --files, and --lines.\n\     -O EXTENSIONS, --file-extension=EXTENSIONS\n\             Search only files whose filename extensions match the specified\n\             comma-separated list of EXTENSIONS, same as --include='*.ext' for\n\@@ -10831,6 +11133,15 @@ void help(std::ostream& out)             The line number of the matching line in the file is output without\n\             displaying the match.  The line number counter is reset for each\n\             file processed.\n\+    --files\n\+            Apply Boolean queries to match files, the opposite of --lines.  A\n\+            file matches if all Boolean conditions are satisfied by the lines\n\+            matched in the file.  For example, --files -e A --and -e B -e C\n\+            --andnot -e D matches a file if some lines match `A' and some lines\n\+            match (`B' or `C') and no line in the file matches `D'.  May also\n\+            be specified as --files --bool 'A B|C -D'.  Option -v cannot be\n\+            specified with --files.  See also options --and, --andnot, --not,\n\+            --bool and --lines.\n\     -P, --perl-regexp\n\             Interpret PATTERN as a Perl regular expression" #if defined(HAVE_PCRE2)

src/ugrep.hpp

@@ -38,7 +38,7 @@ #define UGREP_HPP  // ugrep version-#define UGREP_VERSION "3.3.12"+#define UGREP_VERSION "3.4.0"  // disable mmap because mmap is almost always slower than the file reading speed improvements since 3.0.0 #define WITH_NO_MMAP

tests/archive.gz

Binary file changed

tests/archive.tar.zip

Binary file changed

tests/archive.tgz

Binary file changed

tests/gen.sh

@@ -124,9 +124,12 @@ for PAT in '' 'Hello' '\w+\s+\S+' '\S\n\S' 'nomatch' ; do   $UG -U -IPx "$PAT" $FILES > "out/$FN-IPx.out" done -for PAT in '' 'Hello World' 'Hello -World' 'Hello -World|greeting' 'Hello -(greeting|World)' '"a Hello" greeting' ; do+for PAT in '' 'Hello World' 'Hello -World' 'Hello -bin' 'bin -Hello' 'bin -greeting' 'Hello -World|greeting' 'Hello -bin|greeting' 'Hello -(greeting|World)' '"a Hello" greeting' ; do   FN=`echo "Hello_$PAT" | tr -Cd '[:alnum:]_-'`-  $UG -U --bool "$PAT" $FILES > "out/$FN--bool.out"+  for OPS in '' '-l' '-c' '-co' '-o' '-C2' '-y' '--json' ; do+    $UG -U --bool $OPS "$PAT" $FILES > "out/$FN--bool$OPS.out"+    $UG -U --files --bool $OPS "$PAT" $FILES > "out/$FN--files--bool$OPS.out"+  done done  $UG -U -e 'Hello' --and 'World' $FILES > "out/Hello--and.out"

tests/out/Hello_--bool--json.out

new file mode 100644@@ -0,0 +1,186 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "#!/bin/sh" },+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Êþº¾\u0000\u0000\u00004\u0000\u001d" },+      { "match": "\u0000\u0006\u0000\u000f\t\u0000\u0010\u0000\u0011\b\u0000\u0012" },+      { "match": "\u0000\u0013\u0000\u0014\u0007\u0000\u0015\u0007\u0000\u0016\u0001\u0000\u0006<init>\u0001\u0000\u0003()V\u0001\u0000\u0004Code\u0001\u0000\u000fLineNumberTable\u0001\u0000\u0004main\u0001\u0000\u0016([Ljava/lang/String;)V\u0001\u0000" },+      { "match": "SourceFile\u0001\u0000" },+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" },+      { "match": "\u0000\u0000\u0000\u0006\u0000\u0001\u0000\u0000\u0000\u0003\u0000\t\u0000\u000b\u0000\f\u0000\u0001\u0000\t\u0000\u0000\u0000%\u0000\u0002\u0000\u0001\u0000\u0000\u0000\t²\u0000\u0002\u0012\u0003¶\u0000\u0004±\u0000\u0000\u0000\u0001\u0000" },+      { "match": "\u0000\u0000\u0000" },+      { "match": "\u0000\u0002\u0000\u0000\u0000\u0005\u0000\b\u0000\u0006\u0000\u0001\u0000\r\u0000\u0000\u0000\u0002\u0000\u000e" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "import java.lang.*;" },+      { "match": "" },+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "{ public static void main(String[] arg)" },+      { "match": "  { System.out.println(\"Hello World!\");" },+      { "match": "  }" },+      { "match": "}" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "%PDF-1.3" },+      { "match": "%Äåòåë§ó ÐÄÆ" },+      { "match": "4 0 obj" },+      { "match": "<< /Length 5 0 R /Filter /FlateDecode >>" },+      { "match": "stream" },+      { "match": "x\u0001EŽ»" },+      { "match": "\u00021\u0014D{¿â”Z˜½‰y˜V\u0011l\u0017.ø\u0001a-d×eÍÿƒ\u0017,¬†™9\f³1²Q\u0002¹F—â©\u0012ó™�\\LY" },+      { "match": "Ÿ‰\u0007o†k÷´ŽÐ›áâj©Rj²@8þ­í” »¶pQ£DÄ£\rï iÂ\u000bº0¨z¬|²¿Oó¼\u001eÐ\u00177µ7ã\u0017Nÿ ¥" },+      { "match": "endstream" },+      { "match": "endobj" },+      { "match": "5 0 obj" },+      { "match": "123" },+      { "match": "endobj" },+      { "match": "2 0 obj" },+      { "match": "<< /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 612 792]" },+      { "match": ">>" },+      { "match": "endobj" },+      { "match": "6 0 obj" },+      { "match": "<< /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /TT1 8 0 R" },+      { "match": ">> >>" },+      { "match": "endobj" },+      { "match": "9 0 obj" },+      { "match": "<< /Length 10 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >>" },+      { "match": "stream" },+      { "match": "x\u0001…U]h\u001cU\u0014>»sg\u0003\u0012\u0007\u001f´\r-´ƒ?m\té2‰V\u0013‹µÛMºI\u0013·ëfS›*Êtv6;Ídfœ™Ý&¡O¥à›\u0016\u0004é« >Æ‚\b¶*6/ö¥¥Å’J5\u000f" },+      { "match": "\u0011ZŒ (}Rð;3ÛdvA2Ã�ùî¹çž{Îwî¹—¨ëoÝóì´J4ç„~¡œ›>9}JíºMiR¨›ðèFàåJ¥\tÆŽë˜üo\u001eÞ¥\u0014Kîìg[íc[öäª\u0019\u0018к†æV\u0003cŽ(¥\u0011ej†ç‡D]— \u001f<\u001bzŒo\u0003?9[)ç�×€•Ö\\@zª`:¦o\u0019jÁ×\u0017Ô’ïÖ,;éëVãlcËgÎn°¯üìFë\u000ef'Çñï…Ï\u0017Lgj\u0012x\u001fð’¡�0î\u0003¾Û´N\u0014cœN{á‘r¬ŸÎ6f§r-ùÉštª%¿PoŒ2Î\u0012¥—\u0016ë•7€Ÿ\u0000^uN\u0017�·ô×gÝq¶ÓC$õ\u0018Aþ\u0014ð³ÀZÝ\u001cã<©À\u0015ß-³>Ëê9<\u0002ü2ð%+\u001c«\u0000þôCМd9° ÅzžýÄZ\"{F?V\u0002Þ\u0006|È´\u000b¼\u0016숊\u0017–Øæ ð¼c\u0017y-Ä..›A\u0014/b\u0017?…õÊh¬/§C¿ÂsŸ&’÷Ô¬£cÀàD\u001e­û£,‡?rèÙÑÞz\u0011xÉo”9ö=Àkº?R\u0000†ÍÌcU}˜y~\u0001x€N¤t2É¥Óø\u001aäпˆ7 ‹š\u0011òÈÇX\r}›" },+      { "match": "ÐpÐ|ô\fh\u0015€tZ\u0000*%´LôX'ž£R\u0015=•f¢YA„ØÊý¨oP~×§Ô€´Në�Ö�^£_\"É<ýJsèç!m`l¦Ãn\u001e^8´\b\u000fØ\u0013¶ù eÓ\u0015Û…&žG;(&Ä+bP\f‘*^\u0015‡Å!1\fé�8\u0018͉½OúÎ~>ذô.ÖMú¾\f\u000eB¬gÃ3\u0007¬p4\u0001<ø\u0007ëÎFš\t¶.îhìó¼�Þ?ï¿c\u0019·>ø³�½\u0000ú1'¿Á¢\u000bk&%æ^?÷MOÂCuE|õÖ�îëçèx2KQÞª�Y’×äûò" },+      { "match": "¾÷äÕ¤\rùgy\u0015ï½¶\\=Ê\u000bÿ“¹Í!F;’ÍAÎ<›Q´qÆ\u001bÀ!¸©Esö·YL²é¶b´`ÉB”�«¸ÿË\tóÃ<·±Rs.îð¼·?c6Í÷Š\u000f‹t¾O»¬­kŸh?jh+ÚÇ@¿K\u001fJ_JßJW¤«Ò\rR¥kÒ²ô�ô½ô¹ô5z_@º,]éØIqì\u001b»\u0007~Æ{Öhí0f†³\u0014\u0010óÂÚÌ" },+      { "match": "K\u001fñw\u0006c›ù3¡Õ^\u0003�»c-刲SyF\u0019Vv+Ï)\u0013J¯r@9¬lW\u0006Ðú•Qe/Fvn°dc=΀…’g‹¦#®â<±Wu°çÃK\u001dï¦_\\ÃÖ†5ØI=\u000ežÙÚ¦\u000e¯\u0011W¿\u0015Uc¼;]œ\u0006:M!b‹Î\"r\u001f:œ\u001f'ªþÎÙ\\“85Roâ”°Ä.Ñ/ÆZ5˜\u0013\u0007P…ãmõ8ÈUš\u0019É\fgr¤fz3C™þÌ1ÆQ¬Ñù’Ù‹Ñ!|G\u0012Þ3Ë1ÿ›5Äg\u0018ï\u001dæ¨\tl£‡[+4çqß\u0011å]oÁ·fê¡: i/©9\\¯¦:æ\u0018Ù>U·m5\u001a" },+      { "match": "Tß\fL¿iV³Äw7Ï#úëõèNNm»a4üf,£Tê&Ñ·�“4" },+      { "match": "endstream" },+      { "match": "endobj" },+      { "match": "10 0 obj" },+      { "match": "1116" },+      { "match": "endobj" },+      { "match": "7 0 obj" },+      { "match": "[ /ICCBased 9 0 R ]" },+      { "match": "endobj" },+      { "match": "3 0 obj" },+      { "match": "<< /Type /Pages /MediaBox [0 0 612 792] /Count 1 /Kids [ 2 0 R ] >>" },+      { "match": "endobj" },+      { "match": "11 0 obj" },+      { "match": "<< /Type /Catalog /Pages 3 0 R >>" },+      { "match": "endobj" },+      { "match": "8 0 obj" },+      { "match": "<< /Type /Font /Subtype /TrueType /BaseFont /GQTMZF+Menlo-Regular /FontDescriptor" },+      { "match": "12 0 R /Encoding /MacRomanEncoding /FirstChar 72 /LastChar 111 /Widths [ 602" },+      { "match": "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 602 0 0 0 0 0 0 602" },+      { "match": "0 0 602 ] >>" },+      { "match": "endobj" },+      { "match": "12 0 obj" },+      { "match": "<< /Type /FontDescriptor /FontName /GQTMZF+Menlo-Regular /Flags 33 /FontBBox" },+      { "match": "[-558 -375 718 1041] /ItalicAngle 0 /Ascent 928 /Descent -236 /CapHeight 729" },+      { "match": "/StemV 99 /XHeight 547 /StemH 83 /AvgWidth 602 /MaxWidth 602 /FontFile2 13 0 R" },+      { "match": ">>" },+      { "match": "endobj" },+      { "match": "13 0 obj" },+      { "match": "<< /Length 14 0 R /Length1 4064 /Filter /FlateDecode >>" },+      { "match": "stream" },+      { "match": "x\u0001ÝW{xTÅ\u0015?sw6¯Í>’MHH€]Öå•,�„·R\u0016Hxˆ /ë.ˆòH (!\bA´1�JQ\tÁ@…\bH}\u0015Q©µ+E]\tÄT¤b#E„ \u0002" },+      { "match": "ø¢hŠ\u0014!Ø\u0000CϽ\u001bý>í×ïë¿íÜ�3çüæÌyÝ»÷Δ-\\\\DVª$P`VÉŒ\u0005d6ëI\u001eŠgÝSæ6Eн—HË�½`NITŽ‘åNsæÝ7;*'Nfy@qь¨LWxìWÌ@T\u0016}x¼®¸¤Œí\u0018Íú.\u0013×¼ÒYmó‰£\f°dƽmþé8Ëîù3JŠxäf�0é¶ tQ™)’½šÇì\u0005\u000b‹ÚôE�(¦St®�\u001a–\u0005w©Y)‹î\"\u000biä �”ÄЭ¬+yÖ˜×b²–>}ö�;ì7\\¤N±æâ½eéy\u0006s(¸¤ßå•W…¬�½…Åè¤1ÁëbJT\u0007\"ýàå•×’e½iɘù¾É\bÅf½®UŠ”íë¦É¡™\"…j\tL+I\u0017.RÌ'›4‰\u0003‚pš¼Ã¤vÚĈÍä\u0013·=R\u000eõ‰Dª`ÌJ>¦\t”Ë4Þ´\u0017gjÅ’�‘\u0018“·˜:Òäu\u0013‡‰h&\"\u0002!\u0005¥pµ\u0002W\u0014.+´æâŸuø®\u0002—Zªå%…K\rzËÅ�l©FK¥~ñB\u0017y1„‹\u0001ýB\u0017|{>G~ÛŠó9ø‡Â9…orqÖ…¿×¢™ClVhŽ\\;\u0018¸¦=\u0012_�)”_ÕâL!þ¦púË\fyZáË\f|¡ðù]øLáÓ:œ:™.Oµâd:NÔâ\u0013…�\u0015Ž\u001fK‘Ç\u0015Ž¥àh->ú0E~¤ðáê\u0004ùa" },+      { "match": ">¨À‘Ahb¡i\u0010\u000e+\u001cz?^\u001eRx?\u001e\u0007\u0015ÞS8På”\u00072ñ×TìWx·\u0016�«|²Qá/" },+      { "match": "ïT`ŸÂÛ" },+      { "match": "VØ»)Q¾¥°GáM…?)4°½\u0006\u0017Þ°¢~w�¬WؽkšÜ]‡Ý•ú®:ŸÜ5\r»\u0002z�\u000f;\u0015^¯E¤f¨|MáU\u001e^mÅ+lk‡Â\u001f\u000b±½\u0010/Û\u0010NÂ\u001f\u0014^R�«ø½Â‹" },+      { "match": "¿KÂ6…\u0017ž·É\u0017rñ¼\rÏmuÊçºa«\u0013ÏnñËg+°Å�ß*<£ð´ÂSO¦Ë§" },+      { "match": "ñä\u0013\u000eùd:žpà7ñجð8;y\\aS\"6nè)7*lè‰ÇØÿcµ¨]_'k\u0015Öó³µ¾\u000eë+õuk|rÝ4¬\u000bè�*üZa-Ëkë°Æ‡\u001a.FÍP<ÂÙ>âÂê\u0004T3P]ˆU\\´U>T9±Ráa…‡\u0014\u001e\\á”\u000f*¬pâW" },+      { "match": "Ë\u0015\u001ep\u000e“\u000fLÂ/\u0015*ïŲ¥\u0015r™ÂÒ" },+      { "match": "TtÄý" },+      { "match": "å6üBa‰Â=" },+      { "match": "‹Ë¬r±\u001d‹#‚\u0002Gõ2+Ê\u001aôEIX\u0014Ð\u0017*Ü­°@¡tþ$YZ‹ù%ÝäüI(é†y" },+      { "match": "wåâN…¹¹(nÅœ:ÌV(R(T˜5³£œ¥0“\u001crfGÌP˜®p‡ÂíS\u0012äí6L+Ämû0•…©.LI\u0000?ÑA\u0017nUø¹Â-\u0019éò–\\LV˜¤0QaB\u0005Æ+ÜìÂ8…±Â/Ç*ÜT‡1Ýpãè4ycŒ\u001ež$G§aTAš\u001c¥0’¥‘…\u0018ÁÒˆ:\u0014¤!Ÿ�üþ\u0018>Ì)‡'axD\u000b\u0004âôaCír˜\u0013Ã\"\u001a±44`“Cí\u0018\u001a\u0011\r,\u0005†XeÀ†@DT²4Ä\u001a'‡X1$\"\u0002�Býg" },+      { "match": "ƒ9„Á­¸Aáún\u0018¤0�\u000b<°\u0010\u0003z·—\u0003Æ ¿B?¿KöSè;\u0006}zµ—}Æ �‡<…\\VÌUèÍÓ½Û£W{ä0—“†žq©²g\u001düÙÉÒï‚?¢\u0019n³\u001dN™�Œl#ÜZ=«‡Of)ô`Í\u001e>t×\u0006Éî" },+      { "match": "Ý\u0014º*t±Ã—:Lú" },+      { "match": "p�\u001d^…Îv»ì¬àqû¥§\u0002n?:�AGöÜQ¡ƒB&×6S!ƒïJF:Ú+¤+¤)´c\u000bíF 5Å/S‡!Åå�)~¸\u001cHf½d\u0017’x}’‚“3w\u000eƒƒ=8œpDkg·Y¥Ý\u000e{´v¶Äxi³Â\u0016­]\"×.1\u001e‰\\»\u001dº5\u000eVãÙê¯'(Äs&ñ" },+      { "match": "q©ˆu FÁ¦-" },+      { "match": "Ò\u0005prh…Æ€6\b‚\u0003\u0010~�\u0003\"\"" },+      { "match": "W¬\u0016Yÿ?�þÇSáOg„\u001a;M¬åÑØGDè!m\u0019¥¿¿\"´‡u4S/\"\u001aÅJ±“ù­¼·h¤åt^ÄãmÑŸ¹z^\u001bÔ=ŒÖÐfsu\rNÓbì¢Cô\u000e\u001dcî´\u0018\b^+\u000e‘Gœ`?+ð¡¡ž¥=LËQ� è$Jh‹x‰-–SD”Ò2�Gm\"[Þ¯\u001fdt?=Ä×£´…J™72XÎñL;h\u0015] \rÚ\u0019šÂüNÚËñ(þüš¹ˆ&jaKÛ´ÁÚlÖÛËÖ6Ñ&±œšh‘Nü)WtJ6iYlu\u0007g@4“6Ë&¹Á¨\u0007�Mò\u001cÏ\u0010u°D,®\u0018/gaÔn«Ø%zkãè\u0010¯/§É¸\rwã˜X¡{õ%8C5\u001aa:ÝI\u0007d“ÅE51^ª±Ì\u0016÷éÓÍ«œ­•kKôéb\u001b�a›3ñ\u001dË\u001eŽl³™1Ñ\u000em¢\u001c'Çqγ\u0019ÛlÒš(µ8h?Z¹îk5%Fé#0„ó)×o¢\rô\fÛíÊ•!*E_ö^Jåruô¢m|ùåjÔrEÍjˆ<m0mÖf‹U\u001cm\u000bW³\u0014ùÔŸ}t�gi…ØÁqSL\u0005-’M¼U$êAôZŒEêü÷¦l·#¬ùF\u0017†\u0003\u0013‚î}!�?û'¢Û\u0011ã\u000eÓøpâ}îȵkãƒz†\f…ef\u0018¾Ø°îóžúO“§üÙcÆ\u0007Ý\u0011Ñ® ¿ÍlÁô|\u0006'\u0005Ù\u0003ÿ\f˜Ý\u00150\u0016\u0005F‡¥�£§‡Ý³ŠÝUŽ*ï *GÑ ?ïÿ²Ç0\u0019\u001f|YˆGB\u0011qmE„ò;¼Î»HÜq;OÇe»Ý\u0005sóÃb:\u000bñÙ\fôð0—�í\u001eÁaŽ˜\u0018ô†ÜUîªÑ…Uî\u0011îâ\u0019…\u001c·9òDQU(‡3˜\u0014œËtrÐ\u0013\u000e„2~`‹B!ûհÃKX½*Ä\u0016îl³À£\tå\\e¥Äì1î0ºŒ\u000fN\b†+ó3Â�üP†Çã.\b7Œ\u000f†\u001bò3<¡\u0010kÙ~ˆ”#®˜›Ö\u0016³�c¶õàyGÔ" },+      { "match": "—(�\u0011¦PU•asRÐë\tWVUeTq\u001emr„\u001a~\u0002\bú)\u0010h\u0003\"dØàJ\u0014ð÷`<\u001bãÁëÉ0\u0000¯Çëá8CùìÛiÜš\u0002ŽÔ\u0013òónœŠ¹¯ä^ÎýœÁó³\u0018Ýçóù‚Ï\u0000SX\u001eEÝþm¿ÎðÙ´\u001féÁ”t“vágw;�\u0014™ü¯/Vµz±Ü§ª\u0018ê´ËÜó\u0013Y„ëU\u0011+—k:å¼u¸¹79\u000e7\u001fnî•ìô8}\u001e§§X§+‹�qå\u000bU\u001bcûîüBKwì0rÐGñ¿ �n6N\u0015|osøÖq·:øXô\u001ewæÅqÆX–<êÜc\u0018\u0017,ǽÇo\u000bCAçná\u001eŸ³“£Š¥„¬^½“óØužÓËÔ»²QëÞØxõƒFÙtõ-í†Ö,훫Nö¯QùµÏø­QN)”IyF\u0004;\u0019t°�\biì!“=eæð;ÏB‰\fî¤dJ7G£\u000e†\u0017‘âI±¤æåöëŸb\u0013^79\u001d”—›\u0014ÓSx;[bôéW>‹}ã¥`CÉÜ=SÕeuT¸Ï\u001d¹\u0014¶®}xÅ‹±Zõ\u0014Ë\u0017û\u0006\f|-+K\f\u0014ÉÂ*\u0002ê“ÇÊ^\bÏ7êrŽë2ÅÒž½v�F•ȷ؈ʨCÜqŽHçSž\u0011Q\"ÅG#q¥^/R¼�»ôuzúz´\u0003SC\u001fœyö\u0015õ‘8!Öß¿ôñCoââ\u001a£æ\u001a­äœGqÎ\tÔŽ¼�dKu\u0012U[ë“6¦Å%Ùó‘”ò³4Ç…æ+Æ-üüB³ãl/ÑÙ’â2rìÛ§K×Ü$§CóvÖœŽ$­¸zÍšêÕkÖ¬>s©å«3--8qôHÓ±cMGŽnVï«OÕ)uHøù³ÓQô4üšíÚRó95N§?n’Å,~~o¦‰Ì\t>:Fu,Æ\u000brä„Éc§ŒÈ\u001a[4^©bÑœÅóf,$ú\u0017 -\u0006ä" },+      { "match": "endstream" },+      { "match": "endobj" },+      { "match": "14 0 obj" },+      { "match": "2734" },+      { "match": "endobj" },+      { "match": "15 0 obj" },+      { "match": "(Hello)" },+      { "match": "endobj" },+      { "match": "16 0 obj" },+      { "match": "(Mac OS X 10.12.6 Quartz PDFContext)" },+      { "match": "endobj" },+      { "match": "17 0 obj" },+      { "match": "(TextEdit)" },+      { "match": "endobj" },+      { "match": "18 0 obj" },+      { "match": "(D:20200124010711Z00'00')" },+      { "match": "endobj" },+      { "match": "19 0 obj" },+      { "match": "()" },+      { "match": "endobj" },+      { "match": "20 0 obj" },+      { "match": "[ ]" },+      { "match": "endobj" },+      { "match": "1 0 obj" },+      { "match": "<< /Title 15 0 R /Producer 16 0 R /Creator 17 0 R /CreationDate 18 0 R /ModDate" },+      { "match": "18 0 R /Keywords 19 0 R /AAPL:Keywords 20 0 R >>" },+      { "match": "endobj" },+      { "match": "xref" },+      { "match": "0 21" },+      { "match": "0000000000 65535 f " },+      { "match": "0000005392 00000 n " },+      { "match": "0000000238 00000 n " },+      { "match": "0000001714 00000 n " },+      { "match": "0000000022 00000 n " },+      { "match": "0000000219 00000 n " },+      { "match": "0000000342 00000 n " },+      { "match": "0000001679 00000 n " },+      { "match": "0000001847 00000 n " },+      { "match": "0000000439 00000 n " },+      { "match": "0000001658 00000 n " },+      { "match": "0000001797 00000 n " },+      { "match": "0000002110 00000 n " },+      { "match": "0000002362 00000 n " },+      { "match": "0000005186 00000 n " },+      { "match": "0000005207 00000 n " },+      { "match": "0000005231 00000 n " },+      { "match": "0000005284 00000 n " },+      { "match": "0000005311 00000 n " },+      { "match": "0000005353 00000 n " },+      { "match": "0000005372 00000 n " },+      { "match": "trailer" },+      { "match": "<< /Size 21 /Root 11 0 R /Info 1 0 R /ID [ <8db88d3335070bd9583341583cf769a3>" },+      { "match": "<8db88d3335070bd9583341583cf769a3> ] >>" },+      { "match": "startxref" },+      { "match": "5536" },+      { "match": "%%EOF" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "#!/bin/sh" },+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_--bool-C2.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat:#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:import java.lang.*;+Hello.java:+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java:  }+Hello.java:}+Binary file Hello.pdf matches+Hello.sh:#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:2+Hello.class:8+Hello.java:7+Hello.pdf:134+Hello.sh:2+Hello.txt:1

tests/out/Hello_--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:2+Hello.class:8+Hello.java:7+Hello.pdf:134+Hello.sh:2+Hello.txt:1

tests/out/Hello_--bool-l.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat+Hello.class+Hello.java+Hello.pdf+Hello.sh+Hello.txt

tests/out/Hello_--bool-o.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat:#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:import java.lang.*;+Hello.java:+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java:  }+Hello.java:}+Binary file Hello.pdf matches+Hello.sh:#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_--bool-y.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat:#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:import java.lang.*;+Hello.java:+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java:  }+Hello.java:}+Binary file Hello.pdf matches+Hello.sh:#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_--files--bool--json.out

new file mode 100644@@ -0,0 +1,186 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "#!/bin/sh" },+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Êþº¾\u0000\u0000\u00004\u0000\u001d" },+      { "match": "\u0000\u0006\u0000\u000f\t\u0000\u0010\u0000\u0011\b\u0000\u0012" },+      { "match": "\u0000\u0013\u0000\u0014\u0007\u0000\u0015\u0007\u0000\u0016\u0001\u0000\u0006<init>\u0001\u0000\u0003()V\u0001\u0000\u0004Code\u0001\u0000\u000fLineNumberTable\u0001\u0000\u0004main\u0001\u0000\u0016([Ljava/lang/String;)V\u0001\u0000" },+      { "match": "SourceFile\u0001\u0000" },+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" },+      { "match": "\u0000\u0000\u0000\u0006\u0000\u0001\u0000\u0000\u0000\u0003\u0000\t\u0000\u000b\u0000\f\u0000\u0001\u0000\t\u0000\u0000\u0000%\u0000\u0002\u0000\u0001\u0000\u0000\u0000\t²\u0000\u0002\u0012\u0003¶\u0000\u0004±\u0000\u0000\u0000\u0001\u0000" },+      { "match": "\u0000\u0000\u0000" },+      { "match": "\u0000\u0002\u0000\u0000\u0000\u0005\u0000\b\u0000\u0006\u0000\u0001\u0000\r\u0000\u0000\u0000\u0002\u0000\u000e" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "import java.lang.*;" },+      { "match": "" },+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "{ public static void main(String[] arg)" },+      { "match": "  { System.out.println(\"Hello World!\");" },+      { "match": "  }" },+      { "match": "}" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "%PDF-1.3" },+      { "match": "%Äåòåë§ó ÐÄÆ" },+      { "match": "4 0 obj" },+      { "match": "<< /Length 5 0 R /Filter /FlateDecode >>" },+      { "match": "stream" },+      { "match": "x\u0001EŽ»" },+      { "match": "\u00021\u0014D{¿â”Z˜½‰y˜V\u0011l\u0017.ø\u0001a-d×eÍÿƒ\u0017,¬†™9\f³1²Q\u0002¹F—â©\u0012ó™�\\LY" },+      { "match": "Ÿ‰\u0007o†k÷´ŽÐ›áâj©Rj²@8þ­í” »¶pQ£DÄ£\rï iÂ\u000bº0¨z¬|²¿Oó¼\u001eÐ\u00177µ7ã\u0017Nÿ ¥" },+      { "match": "endstream" },+      { "match": "endobj" },+      { "match": "5 0 obj" },+      { "match": "123" },+      { "match": "endobj" },+      { "match": "2 0 obj" },+      { "match": "<< /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 612 792]" },+      { "match": ">>" },+      { "match": "endobj" },+      { "match": "6 0 obj" },+      { "match": "<< /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 7 0 R >> /Font << /TT1 8 0 R" },+      { "match": ">> >>" },+      { "match": "endobj" },+      { "match": "9 0 obj" },+      { "match": "<< /Length 10 0 R /N 1 /Alternate /DeviceGray /Filter /FlateDecode >>" },+      { "match": "stream" },+      { "match": "x\u0001…U]h\u001cU\u0014>»sg\u0003\u0012\u0007\u001f´\r-´ƒ?m\té2‰V\u0013‹µÛMºI\u0013·ëfS›*Êtv6;Ídfœ™Ý&¡O¥à›\u0016\u0004é« >Æ‚\b¶*6/ö¥¥Å’J5\u000f" },+      { "match": "\u0011ZŒ (}Rð;3ÛdvA2Ã�ùî¹çž{Îwî¹—¨ëoÝóì´J4ç„~¡œ›>9}JíºMiR¨›ðèFàåJ¥\tÆŽë˜üo\u001eÞ¥\u0014Kîìg[íc[öäª\u0019\u0018к†æV\u0003cŽ(¥\u0011ej†ç‡D]— \u001f<\u001bzŒo\u0003?9[)ç�×€•Ö\\@zª`:¦o\u0019jÁ×\u0017Ô’ïÖ,;éëVãlcËgÎn°¯üìFë\u000ef'Çñï…Ï\u0017Lgj\u0012x\u001fð’¡�0î\u0003¾Û´N\u0014cœN{á‘r¬ŸÎ6f§r-ùÉštª%¿PoŒ2Î\u0012¥—\u0016ë•7€Ÿ\u0000^uN\u0017�·ô×gÝq¶ÓC$õ\u0018Aþ\u0014ð³ÀZÝ\u001cã<©À\u0015ß-³>Ëê9<\u0002ü2ð%+\u001c«\u0000þôCМd9° ÅzžýÄZ\"{F?V\u0002Þ\u0006|È´\u000b¼\u0016숊\u0017–Øæ ð¼c\u0017y-Ä..›A\u0014/b\u0017?…õÊh¬/§C¿ÂsŸ&’÷Ô¬£cÀàD\u001e­û£,‡?rèÙÑÞz\u0011xÉo”9ö=Àkº?R\u0000†ÍÌcU}˜y~\u0001x€N¤t2É¥Óø\u001aäпˆ7 ‹š\u0011òÈÇX\r}›" },+      { "match": "ÐpÐ|ô\fh\u0015€tZ\u0000*%´LôX'ž£R\u0015=•f¢YA„ØÊý¨oP~×§Ô€´Në�Ö�^£_\"É<ýJsèç!m`l¦Ãn\u001e^8´\b\u000fØ\u0013¶ù eÓ\u0015Û…&žG;(&Ä+bP\f‘*^\u0015‡Å!1\fé�8\u0018͉½OúÎ~>ذô.ÖMú¾\f\u000eB¬gÃ3\u0007¬p4\u0001<ø\u0007ëÎFš\t¶.îhìó¼�Þ?ï¿c\u0019·>ø³�½\u0000ú1'¿Á¢\u000bk&%æ^?÷MOÂCuE|õÖ�îëçèx2KQÞª�Y’×äûò" },+      { "match": "¾÷äÕ¤\rùgy\u0015ï½¶\\=Ê\u000bÿ“¹Í!F;’ÍAÎ<›Q´qÆ\u001bÀ!¸©Esö·YL²é¶b´`ÉB”�«¸ÿË\tóÃ<·±Rs.îð¼·?c6Í÷Š\u000f‹t¾O»¬­kŸh?jh+ÚÇ@¿K\u001fJ_JßJW¤«Ò\rR¥kÒ²ô�ô½ô¹ô5z_@º,]éØIqì\u001b»\u0007~Æ{Öhí0f†³\u0014\u0010óÂÚÌ" },+      { "match": "K\u001fñw\u0006c›ù3¡Õ^\u0003�»c-刲SyF\u0019Vv+Ï)\u0013J¯r@9¬lW\u0006Ðú•Qe/Fvn°dc=΀…’g‹¦#®â<±Wu°çÃK\u001dï¦_\\ÃÖ†5ØI=\u000ežÙÚ¦\u000e¯\u0011W¿\u0015Uc¼;]œ\u0006:M!b‹Î\"r\u001f:œ\u001f'ªþÎÙ\\“85Roâ”°Ä.Ñ/ÆZ5˜\u0013\u0007P…ãmõ8ÈUš\u0019É\fgr¤fz3C™þÌ1ÆQ¬Ñù’Ù‹Ñ!|G\u0012Þ3Ë1ÿ›5Äg\u0018ï\u001dæ¨\tl£‡[+4çqß\u0011å]oÁ·fê¡: i/©9\\¯¦:æ\u0018Ù>U·m5\u001a" },+      { "match": "Tß\fL¿iV³Äw7Ï#úëõèNNm»a4üf,£Tê&Ñ·�“4" },+      { "match": "endstream" },+      { "match": "endobj" },+      { "match": "10 0 obj" },+      { "match": "1116" },+      { "match": "endobj" },+      { "match": "7 0 obj" },+      { "match": "[ /ICCBased 9 0 R ]" },+      { "match": "endobj" },+      { "match": "3 0 obj" },+      { "match": "<< /Type /Pages /MediaBox [0 0 612 792] /Count 1 /Kids [ 2 0 R ] >>" },+      { "match": "endobj" },+      { "match": "11 0 obj" },+      { "match": "<< /Type /Catalog /Pages 3 0 R >>" },+      { "match": "endobj" },+      { "match": "8 0 obj" },+      { "match": "<< /Type /Font /Subtype /TrueType /BaseFont /GQTMZF+Menlo-Regular /FontDescriptor" },+      { "match": "12 0 R /Encoding /MacRomanEncoding /FirstChar 72 /LastChar 111 /Widths [ 602" },+      { "match": "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 602 0 0 0 0 0 0 602" },+      { "match": "0 0 602 ] >>" },+      { "match": "endobj" },+      { "match": "12 0 obj" },+      { "match": "<< /Type /FontDescriptor /FontName /GQTMZF+Menlo-Regular /Flags 33 /FontBBox" },+      { "match": "[-558 -375 718 1041] /ItalicAngle 0 /Ascent 928 /Descent -236 /CapHeight 729" },+      { "match": "/StemV 99 /XHeight 547 /StemH 83 /AvgWidth 602 /MaxWidth 602 /FontFile2 13 0 R" },+      { "match": ">>" },+      { "match": "endobj" },+      { "match": "13 0 obj" },+      { "match": "<< /Length 14 0 R /Length1 4064 /Filter /FlateDecode >>" },+      { "match": "stream" },+      { "match": "x\u0001ÝW{xTÅ\u0015?sw6¯Í>’MHH€]Öå•,�„·R\u0016Hxˆ /ë.ˆòH (!\bA´1�JQ\tÁ@…\bH}\u0015Q©µ+E]\tÄT¤b#E„ \u0002" },+      { "match": "ø¢hŠ\u0014!Ø\u0000CϽ\u001bý>í×ïë¿íÜ�3çüæÌyÝ»÷Δ-\\\\DVª$P`VÉŒ\u0005d6ëI\u001eŠgÝSæ6Eн—HË�½`NITŽ‘åNsæÝ7;*'Nfy@qь¨LWxìWÌ@T\u0016}x¼®¸¤Œí\u0018Íú.\u0013×¼ÒYmó‰£\f°dƽmþé8Ëîù3JŠxäf�0é¶ tQ™)’½šÇì\u0005\u000b‹ÚôE�(¦St®�\u001a–\u0005w©Y)‹î\"\u000biä �”ÄЭ¬+yÖ˜×b²–>}ö�;ì7\\¤N±æâ½eéy\u0006s(¸¤ßå•W…¬�½…Åè¤1ÁëbJT\u0007\"ýàå•×’e½iɘù¾É\bÅf½®UŠ”íë¦É¡™\"…j\tL+I\u0017.RÌ'›4‰\u0003‚pš¼Ã¤vÚĈÍä\u0013·=R\u000eõ‰Dª`ÌJ>¦\t”Ë4Þ´\u0017gjÅ’�‘\u0018“·˜:Òäu\u0013‡‰h&\"\u0002!\u0005¥pµ\u0002W\u0014.+´æâŸuø®\u0002—Zªå%…K\rzËÅ�l©FK¥~ñB\u0017y1„‹\u0001ýB\u0017|{>G~ÛŠó9ø‡Â9…orqÖ…¿×¢™ClVhŽ\\;\u0018¸¦=\u0012_�)”_ÕâL!þ¦púË\fyZáË\f|¡ðù]øLáÓ:œ:™.Oµâd:NÔâ\u0013…�\u0015Ž\u001fK‘Ç\u0015Ž¥àh->ú0E~¤ðáê\u0004ùa" },+      { "match": ">¨À‘Ahb¡i\u0010\u000e+\u001cz?^\u001eRx?\u001e\u0007\u0015ÞS8På”\u00072ñ×TìWx·\u0016�«|²Qá/" },+      { "match": "ïT`ŸÂÛ" },+      { "match": "VØ»)Q¾¥°GáM…?)4°½\u0006\u0017Þ°¢~w�¬WؽkšÜ]‡Ý•ú®:ŸÜ5\r»\u0002z�\u000f;\u0015^¯E¤f¨|MáU\u001e^mÅ+lk‡Â\u001f\u000b±½\u0010/Û\u0010NÂ\u001f\u0014^R�«ø½Â‹" },+      { "match": "¿KÂ6…\u0017ž·É\u0017rñ¼\rÏmuÊçºa«\u0013ÏnñËg+°Å�ß*<£ð´ÂSO¦Ë§" },+      { "match": "ñä\u0013\u000eùd:žpà7ñجð8;y\\aS\"6nè)7*lè‰ÇØÿcµ¨]_'k\u0015Öó³µ¾\u000eë+õuk|rÝ4¬\u000bè�*üZa-Ëkë°Æ‡\u001a.FÍP<ÂÙ>âÂê\u0004T3P]ˆU\\´U>T9±Ráa…‡\u0014\u001e\\á”\u000f*¬pâW" },+      { "match": "Ë\u0015\u001ep\u000e“\u000fLÂ/\u0015*ïŲ¥\u0015r™ÂÒ" },+      { "match": "TtÄý" },+      { "match": "å6üBa‰Â=" },+      { "match": "‹Ë¬r±\u001d‹#‚\u0002Gõ2+Ê\u001aôEIX\u0014Ð\u0017*Ü­°@¡tþ$YZ‹ù%ÝäüI(é†y" },+      { "match": "wåâN…¹¹(nÅœ:ÌV(R(T˜5³£œ¥0“\u001crfGÌP˜®p‡ÂíS\u0012äí6L+Ämû0•…©.LI\u0000?ÑA\u0017nUø¹Â-\u0019éò–\\LV˜¤0QaB\u0005Æ+ÜìÂ8…±Â/Ç*ÜT‡1Ýpãè4ycŒ\u001ež$G§aTAš\u001c¥0’¥‘…\u0018ÁÒˆ:\u0014¤!Ÿ�üþ\u0018>Ì)‡'axD\u000b\u0004âôaCír˜\u0013Ã\"\u001a±44`“Cí\u0018\u001a\u0011\r,\u0005†XeÀ†@DT²4Ä\u001a'‡X1$\"\u0002�Býg" },+      { "match": "ƒ9„Á­¸Aáún\u0018¤0�\u000b<°\u0010\u0003z·—\u0003Æ ¿B?¿KöSè;\u0006}zµ—}Æ �‡<…\\VÌUèÍÓ½Û£W{ä0—“†žq©²g\u001düÙÉÒï‚?¢\u0019n³\u001dN™�Œl#ÜZ=«‡Of)ô`Í\u001e>t×\u0006Éî" },+      { "match": "Ý\u0014º*t±Ã—:Lú" },+      { "match": "p�\u001d^…Îv»ì¬àqû¥§\u0002n?:�AGöÜQ¡ƒB&×6S!ƒïJF:Ú+¤+¤)´c\u000bíF 5Å/S‡!Åå�)~¸\u001cHf½d\u0017’x}’‚“3w\u000eƒƒ=8œpDkg·Y¥Ý\u000e{´v¶Äxi³Â\u0016­]\"×.1\u001e‰\\»\u001dº5\u000eVãÙê¯'(Äs&ñ" },+      { "match": "q©ˆu FÁ¦-" },+      { "match": "Ò\u0005prh…Æ€6\b‚\u0003\u0010~�\u0003\"\"" },+      { "match": "W¬\u0016Yÿ?�þÇSáOg„\u001a;M¬åÑØGDè!m\u0019¥¿¿\"´‡u4S/\"\u001aÅJ±“ù­¼·h¤åt^ÄãmÑŸ¹z^\u001bÔ=ŒÖÐfsu\rNÓbì¢Cô\u000e\u001dcî´\u0018\b^+\u000e‘Gœ`?+ð¡¡ž¥=LËQ� è$Jh‹x‰-–SD”Ò2�Gm\"[Þ¯\u001fdt?=Ä×£´…J™72XÎñL;h\u0015] \rÚ\u0019šÂüNÚËñ(þüš¹ˆ&jaKÛ´ÁÚlÖÛËÖ6Ñ&±œšh‘Nü)WtJ6iYlu\u0007g@4“6Ë&¹Á¨\u0007�Mò\u001cÏ\u0010u°D,®\u0018/gaÔn«Ø%zkãè\u0010¯/§É¸\rwã˜X¡{õ%8C5\u001aa:ÝI\u0007d“ÅE51^ª±Ì\u0016÷éÓÍ«œ­•kKôéb\u001b�a›3ñ\u001dË\u001eŽl³™1Ñ\u000em¢\u001c'Çqγ\u0019ÛlÒš(µ8h?Z¹îk5%Fé#0„ó)×o¢\rô\fÛíÊ•!*E_ö^Jåruô¢m|ùåjÔrEÍjˆ<m0mÖf‹U\u001cm\u000bW³\u0014ùÔŸ}t�gi…ØÁqSL\u0005-’M¼U$êAôZŒEêü÷¦l·#¬ùF\u0017†\u0003\u0013‚î}!�?û'¢Û\u0011ã\u000eÓøpâ}îȵkãƒz†\f…ef\u0018¾Ø°îóžúO“§üÙcÆ\u0007Ý\u0011Ñ® ¿ÍlÁô|\u0006'\u0005Ù\u0003ÿ\f˜Ý\u00150\u0016\u0005F‡¥�£§‡Ý³ŠÝUŽ*ï *GÑ ?ïÿ²Ç0\u0019\u001f|YˆGB\u0011qmE„ò;¼Î»HÜq;OÇe»Ý\u0005sóÃb:\u000bñÙ\fôð0—�í\u001eÁaŽ˜\u0018ô†ÜUîªÑ…Uî\u0011îâ\u0019…\u001c·9òDQU(‡3˜\u0014œËtrÐ\u0013\u000e„2~`‹B!ûհÃKX½*Ä\u0016îl³À£\tå\\e¥Äì1î0ºŒ\u000fN\b†+ó3Â�üP†Çã.\b7Œ\u000f†\u001bò3<¡\u0010kÙ~ˆ”#®˜›Ö\u0016³�c¶õàyGÔ" },+      { "match": "—(�\u0011¦PU•asRÐë\tWVUeTq\u001emr„\u001a~\u0002\bú)\u0010h\u0003\"dØàJ\u0014ð÷`<\u001bãÁëÉ0\u0000¯Çëá8CùìÛiÜš\u0002ŽÔ\u0013òónœŠ¹¯ä^ÎýœÁó³\u0018Ýçóù‚Ï\u0000SX\u001eEÝþm¿ÎðÙ´\u001féÁ”t“vágw;�\u0014™ü¯/Vµz±Ü§ª\u0018ê´ËÜó\u0013Y„ëU\u0011+—k:å¼u¸¹79\u000e7\u001fnî•ìô8}\u001e§§X§+‹�qå\u000bU\u001bcûîüBKwì0rÐGñ¿ �n6N\u0015|osøÖq·:øXô\u001ewæÅqÆX–<êÜc\u0018\u0017,ǽÇo\u000bCAçná\u001eŸ³“£Š¥„¬^½“óØužÓËÔ»²QëÞØxõƒFÙtõ-í†Ö,훫Nö¯QùµÏø­QN)”IyF\u0004;\u0019t°�\biì!“=eæð;ÏB‰\fî¤dJ7G£\u000e†\u0017‘âI±¤æåöëŸb\u0013^79\u001d”—›\u0014ÓSx;[bôéW>‹}ã¥`CÉÜ=SÕeuT¸Ï\u001d¹\u0014¶®}xÅ‹±Zõ\u0014Ë\u0017û\u0006\f|-+K\f\u0014ÉÂ*\u0002ê“ÇÊ^\bÏ7êrŽë2ÅÒž½v�F•ȷ؈ʨCÜqŽHçSž\u0011Q\"ÅG#q¥^/R¼�»ôuzúz´\u0003SC\u001fœyö\u0015õ‘8!Öß¿ôñCoââ\u001a£æ\u001a­äœGqÎ\tÔŽ¼�dKu\u0012U[ë“6¦Å%Ùó‘”ò³4Ç…æ+Æ-üüB³ãl/ÑÙ’â2rìÛ§K×Ü$§CóvÖœŽ$­¸zÍšêÕkÖ¬>s©å«3--8qôHÓ±cMGŽnVï«OÕ)uHøù³ÓQô4üšíÚRó95N§?n’Å,~~o¦‰Ì\t>:Fu,Æ\u000brä„Éc§ŒÈ\u001a[4^©bÑœÅóf,$ú\u0017 -\u0006ä" },+      { "match": "endstream" },+      { "match": "endobj" },+      { "match": "14 0 obj" },+      { "match": "2734" },+      { "match": "endobj" },+      { "match": "15 0 obj" },+      { "match": "(Hello)" },+      { "match": "endobj" },+      { "match": "16 0 obj" },+      { "match": "(Mac OS X 10.12.6 Quartz PDFContext)" },+      { "match": "endobj" },+      { "match": "17 0 obj" },+      { "match": "(TextEdit)" },+      { "match": "endobj" },+      { "match": "18 0 obj" },+      { "match": "(D:20200124010711Z00'00')" },+      { "match": "endobj" },+      { "match": "19 0 obj" },+      { "match": "()" },+      { "match": "endobj" },+      { "match": "20 0 obj" },+      { "match": "[ ]" },+      { "match": "endobj" },+      { "match": "1 0 obj" },+      { "match": "<< /Title 15 0 R /Producer 16 0 R /Creator 17 0 R /CreationDate 18 0 R /ModDate" },+      { "match": "18 0 R /Keywords 19 0 R /AAPL:Keywords 20 0 R >>" },+      { "match": "endobj" },+      { "match": "xref" },+      { "match": "0 21" },+      { "match": "0000000000 65535 f " },+      { "match": "0000005392 00000 n " },+      { "match": "0000000238 00000 n " },+      { "match": "0000001714 00000 n " },+      { "match": "0000000022 00000 n " },+      { "match": "0000000219 00000 n " },+      { "match": "0000000342 00000 n " },+      { "match": "0000001679 00000 n " },+      { "match": "0000001847 00000 n " },+      { "match": "0000000439 00000 n " },+      { "match": "0000001658 00000 n " },+      { "match": "0000001797 00000 n " },+      { "match": "0000002110 00000 n " },+      { "match": "0000002362 00000 n " },+      { "match": "0000005186 00000 n " },+      { "match": "0000005207 00000 n " },+      { "match": "0000005231 00000 n " },+      { "match": "0000005284 00000 n " },+      { "match": "0000005311 00000 n " },+      { "match": "0000005353 00000 n " },+      { "match": "0000005372 00000 n " },+      { "match": "trailer" },+      { "match": "<< /Size 21 /Root 11 0 R /Info 1 0 R /ID [ <8db88d3335070bd9583341583cf769a3>" },+      { "match": "<8db88d3335070bd9583341583cf769a3> ] >>" },+      { "match": "startxref" },+      { "match": "5536" },+      { "match": "%%EOF" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "#!/bin/sh" },+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_--files--bool-C2.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat:#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:import java.lang.*;+Hello.java:+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java:  }+Hello.java:}+Binary file Hello.pdf matches+Hello.sh:#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_--files--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:2+Hello.class:8+Hello.java:7+Hello.pdf:134+Hello.sh:2+Hello.txt:1

tests/out/Hello_--files--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:2+Hello.class:8+Hello.java:7+Hello.pdf:134+Hello.sh:2+Hello.txt:1

tests/out/Hello_--files--bool-l.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat+Hello.class+Hello.java+Hello.pdf+Hello.sh+Hello.txt

tests/out/Hello_--files--bool-o.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat:#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:import java.lang.*;+Hello.java:+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java:  }+Hello.java:}+Binary file Hello.pdf matches+Hello.sh:#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_--files--bool-y.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat:#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:import java.lang.*;+Hello.java:+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java:  }+Hello.java:}+Binary file Hello.pdf matches+Hello.sh:#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_--files--bool.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat:#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:import java.lang.*;+Hello.java:+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java:  }+Hello.java:}+Binary file Hello.pdf matches+Hello.sh:#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_-I-c.out

@@ -1,6 +1,4 @@ Hello.bat:2-Hello.class:0 Hello.java:7-Hello.pdf:0 Hello.sh:2 Hello.txt:1

tests/out/Hello_-I-co.out

@@ -1,6 +1,4 @@ Hello.bat:2-Hello.class:0 Hello.java:7-Hello.pdf:0 Hello.sh:2 Hello.txt:1

tests/out/Hello_-I-cv.out

@@ -1,6 +1,4 @@ Hello.bat:0-Hello.class:0 Hello.java:0-Hello.pdf:0 Hello.sh:0 Hello.txt:0

tests/out/Hello_Hello-I-c.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:2-Hello.pdf:0 Hello.sh:1 Hello.txt:1

tests/out/Hello_Hello-I-co.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:3-Hello.pdf:0 Hello.sh:1 Hello.txt:1

tests/out/Hello_Hello-I-cv.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:5-Hello.pdf:0 Hello.sh:1 Hello.txt:0

tests/out/Hello_Hello-World--bool--json.out

new file mode 100644@@ -0,0 +1,14 @@+[+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-World--bool-C2.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-World--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:0+Hello.pdf:1+Hello.sh:0+Hello.txt:1

tests/out/Hello_Hello-World--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:0+Hello.pdf:1+Hello.sh:0+Hello.txt:1

tests/out/Hello_Hello-World--bool-l.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf+Hello.txt

tests/out/Hello_Hello-World--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-World--bool-y.out

new file mode 100644@@ -0,0 +1,13 @@+Hello.bat-#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.java-import java.lang.*;+Hello.java-+Hello.java-public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.sh-#!/bin/sh+Hello.sh-echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-World--files--bool--json.out

new file mode 100644@@ -0,0 +1,14 @@+[+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-World--files--bool-C2.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-World--files--bool-c.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-World--files--bool-co.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-World--files--bool-l.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf+Hello.txt

tests/out/Hello_Hello-World--files--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-World--files--bool-y.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-World--files--bool.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-Worldgreeting--bool--json.out

new file mode 100644@@ -0,0 +1,20 @@+[+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-Worldgreeting--bool-C2.out

new file mode 100644@@ -0,0 +1,7 @@+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-Worldgreeting--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:1+Hello.pdf:1+Hello.sh:0+Hello.txt:1

tests/out/Hello_Hello-Worldgreeting--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:3+Hello.pdf:1+Hello.sh:0+Hello.txt:1

tests/out/Hello_Hello-Worldgreeting--bool-l.out

new file mode 100644@@ -0,0 +1,3 @@+Hello.java+Hello.pdf+Hello.txt

tests/out/Hello_Hello-Worldgreeting--bool-o.out

new file mode 100644@@ -0,0 +1,5 @@+Hello.java:Hello+Hello+greeting+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-Worldgreeting--bool-y.out

new file mode 100644@@ -0,0 +1,13 @@+Hello.bat-#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.sh-#!/bin/sh+Hello.sh-echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-Worldgreeting--files--bool--json.out

new file mode 100644@@ -0,0 +1,21 @@+[+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "  { System.out.println(\"Hello World!\");" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-Worldgreeting--files--bool-C2.out

new file mode 100644@@ -0,0 +1,9 @@+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-Worldgreeting--files--bool-c.out

new file mode 100644@@ -0,0 +1,3 @@+Hello.java:2+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-Worldgreeting--files--bool-co.out

new file mode 100644@@ -0,0 +1,3 @@+Hello.java:6+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-Worldgreeting--files--bool-l.out

new file mode 100644@@ -0,0 +1,3 @@+Hello.java+Hello.pdf+Hello.txt

tests/out/Hello_Hello-Worldgreeting--files--bool-o.out

new file mode 100644@@ -0,0 +1,8 @@+Hello.java:Hello+Hello+World+greeting+Hello.java:Hello+World+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-Worldgreeting--files--bool-y.out

new file mode 100644@@ -0,0 +1,9 @@+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-Worldgreeting--files--bool.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:  { System.out.println("Hello World!");+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bin--bool--json.out

new file mode 100644@@ -0,0 +1,39 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "  { System.out.println(\"Hello World!\");" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-bin--bool-C2.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-bin--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:1+Hello.java:2+Hello.pdf:1+Hello.sh:1+Hello.txt:1

tests/out/Hello_Hello-bin--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:3+Hello.java:3+Hello.pdf:1+Hello.sh:1+Hello.txt:1

tests/out/Hello_Hello-bin--bool-l.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat+Hello.class+Hello.java+Hello.pdf+Hello.sh+Hello.txt

tests/out/Hello_Hello-bin--bool-o.out

new file mode 100644@@ -0,0 +1,8 @@+Hello.bat:Hello+Binary file Hello.class matches+Hello.java:Hello+Hello+Hello.java:Hello+Binary file Hello.pdf matches+Hello.sh:Hello+Hello.txt:Hello

tests/out/Hello_Hello-bin--bool-y.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-bin--bool.out

new file mode 100644@@ -0,0 +1,7 @@+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:  { System.out.println("Hello World!");+Binary file Hello.pdf matches+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-bin--files--bool--json.out

new file mode 100644@@ -0,0 +1,27 @@+[+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "  { System.out.println(\"Hello World!\");" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-bin--files--bool-C2.out

new file mode 100644@@ -0,0 +1,10 @@+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bin--files--bool-c.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.class:1+Hello.java:2+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-bin--files--bool-co.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.class:3+Hello.java:3+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-bin--files--bool-l.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.class+Hello.java+Hello.pdf+Hello.txt

tests/out/Hello_Hello-bin--files--bool-o.out

new file mode 100644@@ -0,0 +1,6 @@+Binary file Hello.class matches+Hello.java:Hello+Hello+Hello.java:Hello+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bin--files--bool-y.out

new file mode 100644@@ -0,0 +1,10 @@+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bin--files--bool.out

new file mode 100644@@ -0,0 +1,5 @@+Binary file Hello.class matches+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:  { System.out.println("Hello World!");+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--bool--json.out

new file mode 100644@@ -0,0 +1,39 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "  { System.out.println(\"Hello World!\");" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-bingreeting--bool-C2.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:1+Hello.java:2+Hello.pdf:1+Hello.sh:1+Hello.txt:1

tests/out/Hello_Hello-bingreeting--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:3+Hello.java:4+Hello.pdf:1+Hello.sh:1+Hello.txt:1

tests/out/Hello_Hello-bingreeting--bool-l.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat+Hello.class+Hello.java+Hello.pdf+Hello.sh+Hello.txt

tests/out/Hello_Hello-bingreeting--bool-o.out

new file mode 100644@@ -0,0 +1,9 @@+Hello.bat:Hello+Binary file Hello.class matches+Hello.java:Hello+Hello+greeting+Hello.java:Hello+Binary file Hello.pdf matches+Hello.sh:Hello+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--bool-y.out

new file mode 100644@@ -0,0 +1,14 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--bool.out

new file mode 100644@@ -0,0 +1,7 @@+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:  { System.out.println("Hello World!");+Binary file Hello.pdf matches+Hello.sh:echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--files--bool--json.out

new file mode 100644@@ -0,0 +1,27 @@+[+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "  { System.out.println(\"Hello World!\");" }+    ]+  },+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-bingreeting--files--bool-C2.out

new file mode 100644@@ -0,0 +1,10 @@+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--files--bool-c.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.class:1+Hello.java:2+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-bingreeting--files--bool-co.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.class:3+Hello.java:4+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-bingreeting--files--bool-l.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.class+Hello.java+Hello.pdf+Hello.txt

tests/out/Hello_Hello-bingreeting--files--bool-o.out

new file mode 100644@@ -0,0 +1,7 @@+Binary file Hello.class matches+Hello.java:Hello+Hello+greeting+Hello.java:Hello+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--files--bool-y.out

new file mode 100644@@ -0,0 +1,10 @@+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-bingreeting--files--bool.out

new file mode 100644@@ -0,0 +1,5 @@+Binary file Hello.class matches+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:  { System.out.println("Hello World!");+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-greetingWorld--bool--json.out

new file mode 100644@@ -0,0 +1,14 @@+[+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-greetingWorld--bool-C2.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-greetingWorld--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:0+Hello.pdf:1+Hello.sh:0+Hello.txt:1

tests/out/Hello_Hello-greetingWorld--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:0+Hello.pdf:1+Hello.sh:0+Hello.txt:1

tests/out/Hello_Hello-greetingWorld--bool-l.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf+Hello.txt

tests/out/Hello_Hello-greetingWorld--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-greetingWorld--bool-y.out

new file mode 100644@@ -0,0 +1,13 @@+Hello.bat-#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.java-import java.lang.*;+Hello.java-+Hello.java-public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Binary file Hello.pdf matches+Hello.sh-#!/bin/sh+Hello.sh-echo "Hello World!"+Hello.txt:Hello

tests/out/Hello_Hello-greetingWorld--files--bool--json.out

new file mode 100644@@ -0,0 +1,14 @@+[+  {+    "file": "Hello.pdf",+    "matches": [+      { "match": "(Hello)" }+    ]+  },+  {+    "file": "Hello.txt",+    "matches": [+      { "match": "Hello" }+    ]+  }+]

tests/out/Hello_Hello-greetingWorld--files--bool-C2.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-greetingWorld--files--bool-c.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-greetingWorld--files--bool-co.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf:1+Hello.txt:1

tests/out/Hello_Hello-greetingWorld--files--bool-l.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.pdf+Hello.txt

tests/out/Hello_Hello-greetingWorld--files--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-greetingWorld--files--bool-y.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_Hello-greetingWorld--files--bool.out

new file mode 100644@@ -0,0 +1,2 @@+Binary file Hello.pdf matches+Hello.txt:Hello

tests/out/Hello_HelloWorld--bool--json.out

new file mode 100644@@ -0,0 +1,27 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "  { System.out.println(\"Hello World!\");" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  }+]

tests/out/Hello_HelloWorld--bool-C2.out

new file mode 100644@@ -0,0 +1,12 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"

tests/out/Hello_HelloWorld--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:1+Hello.java:2+Hello.pdf:0+Hello.sh:1+Hello.txt:0

tests/out/Hello_HelloWorld--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:2+Hello.class:4+Hello.java:5+Hello.pdf:0+Hello.sh:2+Hello.txt:0

tests/out/Hello_HelloWorld--bool-l.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat+Hello.class+Hello.java+Hello.sh

tests/out/Hello_HelloWorld--bool-o.out

new file mode 100644@@ -0,0 +1,10 @@+Hello.bat:Hello+World+Binary file Hello.class matches+Hello.java:Hello+Hello+World+Hello.java:Hello+World+Hello.sh:Hello+World

tests/out/Hello_HelloWorld--bool-y.out

new file mode 100644@@ -0,0 +1,13 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"+Hello.txt-Hello

tests/out/Hello_HelloWorld--files--bool--json.out

new file mode 100644@@ -0,0 +1,27 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  },+  {+    "file": "Hello.class",+    "matches": [+      { "match": "Hello.java\f\u0000\u0007\u0000\b\u0007\u0000\u0017\f\u0000\u0018\u0000\u0019\u0001\u0000\fHello World!\u0007\u0000\u001a\f\u0000\u001b\u0000\u001c\u0001\u0000\u0005Hello\u0001\u0000\u0010java/lang/Object\u0001\u0000\u0010java/lang/System\u0001\u0000\u0003out\u0001\u0000\u0015Ljava/io/PrintStream;\u0001\u0000\u0013java/io/PrintStream\u0001\u0000\u0007println\u0001\u0000\u0015(Ljava/lang/String;)V\u0000!\u0000\u0005\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0002\u0000\u0001\u0000\u0007\u0000\b\u0000\u0001\u0000\t\u0000\u0000\u0000\u001d\u0000\u0001\u0000\u0001\u0000\u0000\u0000\u0005*·\u0000\u0001±\u0000\u0000\u0000\u0001\u0000" }+    ]+  },+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" },+      { "match": "  { System.out.println(\"Hello World!\");" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "echo \"Hello World!\"" }+    ]+  }+]

tests/out/Hello_HelloWorld--files--bool-C2.out

new file mode 100644@@ -0,0 +1,12 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"

tests/out/Hello_HelloWorld--files--bool-c.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat:1+Hello.class:1+Hello.java:2+Hello.sh:1

tests/out/Hello_HelloWorld--files--bool-co.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat:2+Hello.class:4+Hello.java:5+Hello.sh:2

tests/out/Hello_HelloWorld--files--bool-l.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat+Hello.class+Hello.java+Hello.sh

tests/out/Hello_HelloWorld--files--bool-o.out

new file mode 100644@@ -0,0 +1,10 @@+Hello.bat:Hello+World+Binary file Hello.class matches+Hello.java:Hello+Hello+World+Hello.java:Hello+World+Hello.sh:Hello+World

tests/out/Hello_HelloWorld--files--bool-y.out

new file mode 100644@@ -0,0 +1,12 @@+Hello.bat-#!/bin/sh+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java:  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Hello.sh-#!/bin/sh+Hello.sh:echo "Hello World!"

tests/out/Hello_HelloWorld--files--bool.out

new file mode 100644@@ -0,0 +1,5 @@+Hello.bat:echo "Hello World!"+Binary file Hello.class matches+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java:  { System.out.println("Hello World!");+Hello.sh:echo "Hello World!"

tests/out/Hello_SnS-I-c.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:2-Hello.pdf:0 Hello.sh:1 Hello.txt:0

tests/out/Hello_SnS-I-co.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:2-Hello.pdf:0 Hello.sh:1 Hello.txt:0

tests/out/Hello_SnS-I-cv.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:5-Hello.pdf:0 Hello.sh:1 Hello.txt:0

tests/out/Hello_aHellogreeting--bool--json.out

new file mode 100644@@ -0,0 +1,8 @@+[+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" }+    ]+  }+]

tests/out/Hello_aHellogreeting--bool-C2.out

new file mode 100644@@ -0,0 +1,5 @@+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");

tests/out/Hello_aHellogreeting--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:1+Hello.pdf:0+Hello.sh:0+Hello.txt:0

tests/out/Hello_aHellogreeting--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:0+Hello.class:0+Hello.java:2+Hello.pdf:0+Hello.sh:0+Hello.txt:0

tests/out/Hello_aHellogreeting--bool-l.out

new file mode 100644@@ -0,0 +1 @@+Hello.java

tests/out/Hello_aHellogreeting--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.java:a Hello+greeting

tests/out/Hello_aHellogreeting--bool-y.out

new file mode 100644@@ -0,0 +1,12 @@+Hello.bat-#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Hello.sh-#!/bin/sh+Hello.sh-echo "Hello World!"+Hello.txt-Hello

tests/out/Hello_aHellogreeting--files--bool--json.out

new file mode 100644@@ -0,0 +1,8 @@+[+  {+    "file": "Hello.java",+    "matches": [+      { "match": "public class Hello // prints a Hello World! greeting" }+    ]+  }+]

tests/out/Hello_aHellogreeting--files--bool-C2.out

new file mode 100644@@ -0,0 +1,5 @@+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");

tests/out/Hello_aHellogreeting--files--bool-c.out

new file mode 100644@@ -0,0 +1 @@+Hello.java:1

tests/out/Hello_aHellogreeting--files--bool-co.out

new file mode 100644@@ -0,0 +1 @@+Hello.java:2

tests/out/Hello_aHellogreeting--files--bool-l.out

new file mode 100644@@ -0,0 +1 @@+Hello.java

tests/out/Hello_aHellogreeting--files--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.java:a Hello+greeting

tests/out/Hello_aHellogreeting--files--bool-y.out

new file mode 100644@@ -0,0 +1,7 @@+Hello.java-import java.lang.*;+Hello.java-+Hello.java:public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}

tests/out/Hello_aHellogreeting--files--bool.out

new file mode 100644@@ -0,0 +1 @@+Hello.java:public class Hello // prints a Hello World! greeting

tests/out/Hello_bin-Hello--bool--json.out

new file mode 100644@@ -0,0 +1,14 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "#!/bin/sh" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "#!/bin/sh" }+    ]+  }+]

tests/out/Hello_bin-Hello--bool-C2.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat:#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.sh:#!/bin/sh+Hello.sh-echo "Hello World!"

tests/out/Hello_bin-Hello--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:0+Hello.java:0+Hello.pdf:0+Hello.sh:1+Hello.txt:0

tests/out/Hello_bin-Hello--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:0+Hello.java:0+Hello.pdf:0+Hello.sh:1+Hello.txt:0

tests/out/Hello_bin-Hello--bool-l.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat+Hello.sh

tests/out/Hello_bin-Hello--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:bin+Hello.sh:bin

tests/out/Hello_bin-Hello--bool-y.out

new file mode 100644@@ -0,0 +1,12 @@+Hello.bat:#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.java-import java.lang.*;+Hello.java-+Hello.java-public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Hello.sh:#!/bin/sh+Hello.sh-echo "Hello World!"+Hello.txt-Hello

tests/out/Hello_bin-Hello--bool.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:#!/bin/sh+Hello.sh:#!/bin/sh

tests/out/Hello_bin-Hello--files--bool--json.out

new file mode 100644@@ -0,0 +1,2 @@+[+]

tests/out/Hello_bin-Hello--files--bool-C2.out

new file mode 100644

tests/out/Hello_bin-Hello--files--bool-c.out

new file mode 100644

tests/out/Hello_bin-Hello--files--bool-co.out

new file mode 100644

tests/out/Hello_bin-Hello--files--bool-l.out

new file mode 100644

tests/out/Hello_bin-Hello--files--bool-o.out

new file mode 100644

tests/out/Hello_bin-Hello--files--bool-y.out

new file mode 100644

tests/out/Hello_bin-Hello--files--bool.out

new file mode 100644

tests/out/Hello_bin-greeting--bool--json.out

new file mode 100644@@ -0,0 +1,14 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "#!/bin/sh" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "#!/bin/sh" }+    ]+  }+]

tests/out/Hello_bin-greeting--bool-C2.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat:#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.sh:#!/bin/sh+Hello.sh-echo "Hello World!"

tests/out/Hello_bin-greeting--bool-c.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:0+Hello.java:0+Hello.pdf:0+Hello.sh:1+Hello.txt:0

tests/out/Hello_bin-greeting--bool-co.out

new file mode 100644@@ -0,0 +1,6 @@+Hello.bat:1+Hello.class:0+Hello.java:0+Hello.pdf:0+Hello.sh:1+Hello.txt:0

tests/out/Hello_bin-greeting--bool-l.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat+Hello.sh

tests/out/Hello_bin-greeting--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:bin+Hello.sh:bin

tests/out/Hello_bin-greeting--bool-y.out

new file mode 100644@@ -0,0 +1,12 @@+Hello.bat:#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.java-import java.lang.*;+Hello.java-+Hello.java-public class Hello // prints a Hello World! greeting+Hello.java-{ public static void main(String[] arg)+Hello.java-  { System.out.println("Hello World!");+Hello.java-  }+Hello.java-}+Hello.sh:#!/bin/sh+Hello.sh-echo "Hello World!"+Hello.txt-Hello

tests/out/Hello_bin-greeting--bool.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:#!/bin/sh+Hello.sh:#!/bin/sh

tests/out/Hello_bin-greeting--files--bool--json.out

new file mode 100644@@ -0,0 +1,14 @@+[+  {+    "file": "Hello.bat",+    "matches": [+      { "match": "#!/bin/sh" }+    ]+  },+  {+    "file": "Hello.sh",+    "matches": [+      { "match": "#!/bin/sh" }+    ]+  }+]

tests/out/Hello_bin-greeting--files--bool-C2.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat:#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.sh:#!/bin/sh+Hello.sh-echo "Hello World!"

tests/out/Hello_bin-greeting--files--bool-c.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:1+Hello.sh:1

tests/out/Hello_bin-greeting--files--bool-co.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:1+Hello.sh:1

tests/out/Hello_bin-greeting--files--bool-l.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat+Hello.sh

tests/out/Hello_bin-greeting--files--bool-o.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:bin+Hello.sh:bin

tests/out/Hello_bin-greeting--files--bool-y.out

new file mode 100644@@ -0,0 +1,4 @@+Hello.bat:#!/bin/sh+Hello.bat-echo "Hello World!"+Hello.sh:#!/bin/sh+Hello.sh-echo "Hello World!"

tests/out/Hello_bin-greeting--files--bool.out

new file mode 100644@@ -0,0 +1,2 @@+Hello.bat:#!/bin/sh+Hello.sh:#!/bin/sh

tests/out/Hello_nomatch-I-c.out

@@ -1,6 +1,4 @@ Hello.bat:0-Hello.class:0 Hello.java:0-Hello.pdf:0 Hello.sh:0 Hello.txt:0

tests/out/Hello_nomatch-I-co.out

@@ -1,6 +1,4 @@ Hello.bat:0-Hello.class:0 Hello.java:0-Hello.pdf:0 Hello.sh:0 Hello.txt:0

tests/out/Hello_nomatch-I-cv.out

@@ -1,6 +1,4 @@ Hello.bat:2-Hello.class:0 Hello.java:7-Hello.pdf:0 Hello.sh:2 Hello.txt:0

tests/out/Hello_wsS-I-c.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:4-Hello.pdf:0 Hello.sh:1 Hello.txt:0

tests/out/Hello_wsS-I-co.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:8-Hello.pdf:0 Hello.sh:1 Hello.txt:0

tests/out/Hello_wsS-I-cv.out

@@ -1,6 +1,4 @@ Hello.bat:1-Hello.class:0 Hello.java:3-Hello.pdf:0 Hello.sh:1 Hello.txt:0

tests/verify.sh

@@ -303,12 +303,17 @@ for PAT in '' 'Hello' '\w+\s+\S+' '\S\n\S' 'nomatch' ; do   fi done -for PAT in '' 'Hello World' 'Hello -World' 'Hello -World|greeting' 'Hello -(greeting|World)' '"a Hello" greeting' ; do+for PAT in '' 'Hello World' 'Hello -World' 'Hello -bin' 'bin -Hello' 'bin -greeting' 'Hello -World|greeting' 'Hello -bin|greeting' 'Hello -(greeting|World)' '"a Hello" greeting' ; do   FN=`echo "Hello_$PAT" | tr -Cd '[:alnum:]_-'`-  printf .-  $UG -U --bool "$PAT" $FILES \-    | $DIFF "out/$FN--bool.out" \-    || ERR "--bool '$PAT' $FILES"+  for OPS in '' '-l' '-c' '-co' '-o' '-C2' '-y' '--json' ; do+    printf .+    $UG -U --bool $OPS "$PAT" $FILES \+      | $DIFF "out/$FN--bool$OPS.out" \+      || ERR "--bool $OPS '$PAT' $FILES"+    $UG -U --files --bool $OPS "$PAT" $FILES \+      | $DIFF "out/$FN--files--bool$OPS.out" \+      || ERR "--files --bool $OPS '$PAT' $FILES"+  done done  printf .