Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ugrep for openSUSE:Factory checked 
in at 2026-04-22 17:02:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ugrep (Old)
 and      /work/SRC/openSUSE:Factory/.ugrep.new.11940 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ugrep"

Wed Apr 22 17:02:05 2026 rev:89 rq:1348752 version:7.7.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/ugrep/ugrep.changes      2026-03-06 
18:19:20.838032496 +0100
+++ /work/SRC/openSUSE:Factory/.ugrep.new.11940/ugrep.changes   2026-04-22 
17:02:40.143608606 +0200
@@ -1,0 +2,14 @@
+Wed Apr 22 08:40:36 UTC 2026 - Andreas Stieger <[email protected]>
+
+- Update to version 7.7.0:
+  * support TUI search on slow and incomplete standard input pipes
+    e.g. from tail -f #540
+  * update options -m (--max-count) and -K (--max-line) to stop
+    reading input upon reaching the stopping criterium, then exit
+    without draining the standard input pipe like GNU grep
+  * improve binary file detection for huge mixed files
+  * local config files located in the working directory are only
+    read when owned by the user running the ugrep process
+  * fix lzma 7zip api memory leak
+
+-------------------------------------------------------------------

Old:
----
  ugrep-7.6.0.obscpio

New:
----
  ugrep-7.7.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ugrep.spec ++++++
--- /var/tmp/diff_new_pack.urNPdn/_old  2026-04-22 17:02:41.107647139 +0200
+++ /var/tmp/diff_new_pack.urNPdn/_new  2026-04-22 17:02:41.111647299 +0200
@@ -23,7 +23,7 @@
 %bcond_without fish
 %endif
 Name:           ugrep
-Version:        7.6.0
+Version:        7.7.0
 Release:        0
 Summary:        Universal grep: a feature-rich grep implementation with focus 
on speed
 License:        BSD-3-Clause

++++++ _service ++++++
--- /var/tmp/diff_new_pack.urNPdn/_old  2026-04-22 17:02:41.179650016 +0200
+++ /var/tmp/diff_new_pack.urNPdn/_new  2026-04-22 17:02:41.187650336 +0200
@@ -5,7 +5,7 @@
     <param name="changesgenerate">enable</param>
     <param name="filename">ugrep</param>
     <param name="versionformat">@PARENT_TAG@</param>
-    <param name="revision">v7.6.0</param>
+    <param name="revision">v7.7.0</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="versionrewrite-replacement">\1</param>
   </service>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.urNPdn/_old  2026-04-22 17:02:41.235652255 +0200
+++ /var/tmp/diff_new_pack.urNPdn/_new  2026-04-22 17:02:41.243652575 +0200
@@ -1,7 +1,7 @@
 <servicedata>
   <service name="tar_scm">
       <param name="url">https://github.com/Genivia/ugrep.git</param>
-      <param 
name="changesrevision">c701fb852c8fe5ea48143bf809596470d5e2b248</param>
+      <param 
name="changesrevision">5bfa7623a98e71c4ec1f2bd3046cb084a09ff82b</param>
   </service>
 </servicedata>
 (No newline at EOF)

++++++ ugrep-7.6.0.obscpio -> ugrep-7.7.0.obscpio ++++++
Binary files old/ugrep-7.6.0/bin/win32/ug.exe and 
new/ugrep-7.7.0/bin/win32/ug.exe differ
Binary files old/ugrep-7.6.0/bin/win32/ugrep-indexer.exe and 
new/ugrep-7.7.0/bin/win32/ugrep-indexer.exe differ
Binary files old/ugrep-7.6.0/bin/win32/ugrep.exe and 
new/ugrep-7.7.0/bin/win32/ugrep.exe differ
Binary files old/ugrep-7.6.0/bin/win64/ug.exe and 
new/ugrep-7.7.0/bin/win64/ug.exe differ
Binary files old/ugrep-7.6.0/bin/win64/ugrep-indexer.exe and 
new/ugrep-7.7.0/bin/win64/ugrep-indexer.exe differ
Binary files old/ugrep-7.6.0/bin/win64/ugrep.exe and 
new/ugrep-7.7.0/bin/win64/ugrep.exe differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/bash/ug 
new/ugrep-7.7.0/completions/bash/ug
--- old/ugrep-7.6.0/completions/bash/ug 2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/completions/bash/ug 2026-04-21 19:29:26.000000000 +0200
@@ -285,7 +285,7 @@
 -J NUM, --jobs=NUM Specifies the number of threads spawned to search files.
 -j, --smart-case Perform case insensitive matching, unless a pattern is 
specified with a literal upper case letter.
 --json Output file matches in JSON.
--K [MIN,][MAX], --range=[MIN,][MAX], --min-line=MIN, --max-line=MAX Start 
searching at line MIN, stop at line MAX when specified.
+-K [MIN,][MAX], --range=[MIN,][MAX], --min-line=MIN, --max-line=MAX Start 
searching at line MIN, stop reading input after line MAX.
 -k, --column-number The column number of a pattern match is displayed in front 
of the respective matched line, starting at column 1.
 -L, --files-without-match Only the names of files not containing selected 
lines are written to standard output.
 -l, --files-with-matches Only the names of files containing selected lines are 
written to standard output.
@@ -293,7 +293,7 @@
 --line-buffered Force output to be line buffered instead of block buffered.
 --lines Boolean line matching mode for option --bool, the default mode.
 -M MAGIC, --file-magic=MAGIC Only search files matching the magic signature 
pattern MAGIC.
--m [MIN,][MAX], --min-count=MIN, --max-count=MAX Require MIN matches, stop 
after MAX matches when specified.
+-m [MIN,][MAX], --min-count=MIN, --max-count=MAX Require MIN matches, stop 
reading input upon MAX matches.
 --match Match all lines.
 --max-files=NUM Restrict the number of files matched to NUM.
 --max-size=MAX Only search files whose physical size does not exceed MAX bytes.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/fish/ug+.fish 
new/ugrep-7.7.0/completions/fish/ug+.fish
--- old/ugrep-7.6.0/completions/fish/ug+.fish   2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/completions/fish/ug+.fish   2026-04-21 19:29:26.000000000 
+0200
@@ -63,7 +63,7 @@
 complete -c ug+ -s J -r -l jobs -d 'Specifies the number of threads spawned to 
search files'
 complete -c ug+ -s j -l smart-case -d 'Perform case insensitive matching, 
unless a pattern is specified with a literal upper case letter'
 complete -c ug+ -l json -d 'Output file matches in JSON'
-complete -c ug+ -s K -r -l range -l min-line -l max-line -d 'Start searching 
at line MIN, stop at line MAX when specified'
+complete -c ug+ -s K -r -l range -l min-line -l max-line -d 'Start searching 
at line MIN, stop reading input after line MAX'
 complete -c ug+ -s k -l column-number -d 'The column number of a pattern match 
is displayed in front of the respective matched line, starting at column 1'
 complete -c ug+ -s L -l files-without-match -d 'Only the names of files not 
containing selected lines are written to standard output'
 complete -c ug+ -s l -l files-with-matches -d 'Only the names of files 
containing selected lines are written to standard output'
@@ -71,7 +71,7 @@
 complete -c ug+ -l line-buffered -d 'Force output to be line buffered instead 
of block buffered'
 complete -c ug+ -l lines -d 'Boolean line matching mode for option --bool, the 
default mode'
 complete -c ug+ -s M -r -l file-magic -d 'Only search files matching the magic 
signature pattern MAGIC'
-complete -c ug+ -s m -r -l min-count -l max-count -d 'Require MIN matches, 
stop after MAX matches when specified'
+complete -c ug+ -s m -r -l min-count -l max-count -d 'Require MIN matches, 
stop reading input upon MAX matches'
 complete -c ug+ -l match -d 'Match all lines'
 complete -c ug+ -l max-files -d 'Restrict the number of files matched to NUM'
 complete -c ug+ -l max-size -d 'Only search files whose physical size does not 
exceed MAX bytes'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/fish/ug.fish 
new/ugrep-7.7.0/completions/fish/ug.fish
--- old/ugrep-7.6.0/completions/fish/ug.fish    2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/completions/fish/ug.fish    2026-04-21 19:29:26.000000000 
+0200
@@ -63,7 +63,7 @@
 complete -c ug -s J -r -l jobs -d 'Specifies the number of threads spawned to 
search files'
 complete -c ug -s j -l smart-case -d 'Perform case insensitive matching, 
unless a pattern is specified with a literal upper case letter'
 complete -c ug -l json -d 'Output file matches in JSON'
-complete -c ug -s K -r -l range -l min-line -l max-line -d 'Start searching at 
line MIN, stop at line MAX when specified'
+complete -c ug -s K -r -l range -l min-line -l max-line -d 'Start searching at 
line MIN, stop reading input after line MAX'
 complete -c ug -s k -l column-number -d 'The column number of a pattern match 
is displayed in front of the respective matched line, starting at column 1'
 complete -c ug -s L -l files-without-match -d 'Only the names of files not 
containing selected lines are written to standard output'
 complete -c ug -s l -l files-with-matches -d 'Only the names of files 
containing selected lines are written to standard output'
@@ -71,7 +71,7 @@
 complete -c ug -l line-buffered -d 'Force output to be line buffered instead 
of block buffered'
 complete -c ug -l lines -d 'Boolean line matching mode for option --bool, the 
default mode'
 complete -c ug -s M -r -l file-magic -d 'Only search files matching the magic 
signature pattern MAGIC'
-complete -c ug -s m -r -l min-count -l max-count -d 'Require MIN matches, stop 
after MAX matches when specified'
+complete -c ug -s m -r -l min-count -l max-count -d 'Require MIN matches, stop 
reading input upon MAX matches'
 complete -c ug -l match -d 'Match all lines'
 complete -c ug -l max-files -d 'Restrict the number of files matched to NUM'
 complete -c ug -l max-size -d 'Only search files whose physical size does not 
exceed MAX bytes'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/fish/ugrep+.fish 
new/ugrep-7.7.0/completions/fish/ugrep+.fish
--- old/ugrep-7.6.0/completions/fish/ugrep+.fish        2026-03-05 
20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/completions/fish/ugrep+.fish        2026-04-21 
19:29:26.000000000 +0200
@@ -63,7 +63,7 @@
 complete -c ugrep+ -s J -r -l jobs -d 'Specifies the number of threads spawned 
to search files'
 complete -c ugrep+ -s j -l smart-case -d 'Perform case insensitive matching, 
unless a pattern is specified with a literal upper case letter'
 complete -c ugrep+ -l json -d 'Output file matches in JSON'
-complete -c ugrep+ -s K -r -l range -l min-line -l max-line -d 'Start 
searching at line MIN, stop at line MAX when specified'
+complete -c ugrep+ -s K -r -l range -l min-line -l max-line -d 'Start 
searching at line MIN, stop reading input after line MAX'
 complete -c ugrep+ -s k -l column-number -d 'The column number of a pattern 
match is displayed in front of the respective matched line, starting at column 
1'
 complete -c ugrep+ -s L -l files-without-match -d 'Only the names of files not 
containing selected lines are written to standard output'
 complete -c ugrep+ -s l -l files-with-matches -d 'Only the names of files 
containing selected lines are written to standard output'
@@ -71,7 +71,7 @@
 complete -c ugrep+ -l line-buffered -d 'Force output to be line buffered 
instead of block buffered'
 complete -c ugrep+ -l lines -d 'Boolean line matching mode for option --bool, 
the default mode'
 complete -c ugrep+ -s M -r -l file-magic -d 'Only search files matching the 
magic signature pattern MAGIC'
-complete -c ugrep+ -s m -r -l min-count -l max-count -d 'Require MIN matches, 
stop after MAX matches when specified'
+complete -c ugrep+ -s m -r -l min-count -l max-count -d 'Require MIN matches, 
stop reading input upon MAX matches'
 complete -c ugrep+ -l match -d 'Match all lines'
 complete -c ugrep+ -l max-files -d 'Restrict the number of files matched to 
NUM'
 complete -c ugrep+ -l max-size -d 'Only search files whose physical size does 
not exceed MAX bytes'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/fish/ugrep.fish 
new/ugrep-7.7.0/completions/fish/ugrep.fish
--- old/ugrep-7.6.0/completions/fish/ugrep.fish 2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/completions/fish/ugrep.fish 2026-04-21 19:29:26.000000000 
+0200
@@ -63,7 +63,7 @@
 complete -c ugrep -s J -r -l jobs -d 'Specifies the number of threads spawned 
to search files'
 complete -c ugrep -s j -l smart-case -d 'Perform case insensitive matching, 
unless a pattern is specified with a literal upper case letter'
 complete -c ugrep -l json -d 'Output file matches in JSON'
-complete -c ugrep -s K -r -l range -l min-line -l max-line -d 'Start searching 
at line MIN, stop at line MAX when specified'
+complete -c ugrep -s K -r -l range -l min-line -l max-line -d 'Start searching 
at line MIN, stop reading input after line MAX'
 complete -c ugrep -s k -l column-number -d 'The column number of a pattern 
match is displayed in front of the respective matched line, starting at column 
1'
 complete -c ugrep -s L -l files-without-match -d 'Only the names of files not 
containing selected lines are written to standard output'
 complete -c ugrep -s l -l files-with-matches -d 'Only the names of files 
containing selected lines are written to standard output'
@@ -71,7 +71,7 @@
 complete -c ugrep -l line-buffered -d 'Force output to be line buffered 
instead of block buffered'
 complete -c ugrep -l lines -d 'Boolean line matching mode for option --bool, 
the default mode'
 complete -c ugrep -s M -r -l file-magic -d 'Only search files matching the 
magic signature pattern MAGIC'
-complete -c ugrep -s m -r -l min-count -l max-count -d 'Require MIN matches, 
stop after MAX matches when specified'
+complete -c ugrep -s m -r -l min-count -l max-count -d 'Require MIN matches, 
stop reading input upon MAX matches'
 complete -c ugrep -l match -d 'Match all lines'
 complete -c ugrep -l max-files -d 'Restrict the number of files matched to NUM'
 complete -c ugrep -l max-size -d 'Only search files whose physical size does 
not exceed MAX bytes'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/zsh/_ug 
new/ugrep-7.7.0/completions/zsh/_ug
--- old/ugrep-7.6.0/completions/zsh/_ug 2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/completions/zsh/_ug 2026-04-21 19:29:26.000000000 +0200
@@ -70,7 +70,7 @@
 {-J,--jobs=-}'[Specifies the number of threads spawned to search files]:NUM:( 
)'
 {-j,--smart-case}'[Perform case insensitive matching, unless a pattern is 
specified with a literal upper case letter]'
 --json'[Output file matches in JSON]'
-{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop at 
line MAX when specified]:MIN,MAX:( )'
+{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop 
reading input after line MAX]:MIN,MAX:( )'
 {-k,--column-number}'[The column number of a pattern match is displayed in 
front of the respective matched line, starting at column 1]'
 {-L,--files-without-match}'[Only the names of files not containing selected 
lines are written to standard output]'
 {-l,--files-with-matches}'[Only the names of files containing selected lines 
are written to standard output]'
@@ -78,7 +78,7 @@
 --line-buffered'[Force output to be line buffered instead of block buffered]'
 --lines'[Boolean line matching mode for option --bool, the default mode]'
 '(1)*'{-M,--file-magic=-}'[Only search files matching the magic signature 
pattern MAGIC]:PATTERN:( )'
-{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop after MAX matches 
when specified]:MIN,MAX:( )::(1 1, 1,10)'
+{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop reading input upon 
MAX matches]:MIN,MAX:( )::(1 1, 1,10)'
 --match'[Match all lines]'
 --max-files=-'[Restrict the number of files matched to NUM]'
 --max-size=-'[Only search files whose physical size does not exceed MAX bytes]'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/zsh/_ug+ 
new/ugrep-7.7.0/completions/zsh/_ug+
--- old/ugrep-7.6.0/completions/zsh/_ug+        2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/completions/zsh/_ug+        2026-04-21 19:29:26.000000000 
+0200
@@ -70,7 +70,7 @@
 {-J,--jobs=-}'[Specifies the number of threads spawned to search files]:NUM:( 
)'
 {-j,--smart-case}'[Perform case insensitive matching, unless a pattern is 
specified with a literal upper case letter]'
 --json'[Output file matches in JSON]'
-{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop at 
line MAX when specified]:MIN,MAX:( )'
+{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop 
reading input after line MAX]:MIN,MAX:( )'
 {-k,--column-number}'[The column number of a pattern match is displayed in 
front of the respective matched line, starting at column 1]'
 {-L,--files-without-match}'[Only the names of files not containing selected 
lines are written to standard output]'
 {-l,--files-with-matches}'[Only the names of files containing selected lines 
are written to standard output]'
@@ -78,7 +78,7 @@
 --line-buffered'[Force output to be line buffered instead of block buffered]'
 --lines'[Boolean line matching mode for option --bool, the default mode]'
 '(1)*'{-M,--file-magic=-}'[Only search files matching the magic signature 
pattern MAGIC]:PATTERN:( )'
-{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop after MAX matches 
when specified]:MIN,MAX:( )::(1 1, 1,10)'
+{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop reading input upon 
MAX matches]:MIN,MAX:( )::(1 1, 1,10)'
 --match'[Match all lines]'
 --max-files=-'[Restrict the number of files matched to NUM]'
 --max-size=-'[Only search files whose physical size does not exceed MAX bytes]'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/zsh/_ugrep 
new/ugrep-7.7.0/completions/zsh/_ugrep
--- old/ugrep-7.6.0/completions/zsh/_ugrep      2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/completions/zsh/_ugrep      2026-04-21 19:29:26.000000000 
+0200
@@ -70,7 +70,7 @@
 {-J,--jobs=-}'[Specifies the number of threads spawned to search files]:NUM:( 
)'
 {-j,--smart-case}'[Perform case insensitive matching, unless a pattern is 
specified with a literal upper case letter]'
 --json'[Output file matches in JSON]'
-{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop at 
line MAX when specified]:MIN,MAX:( )'
+{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop 
reading input after line MAX]:MIN,MAX:( )'
 {-k,--column-number}'[The column number of a pattern match is displayed in 
front of the respective matched line, starting at column 1]'
 {-L,--files-without-match}'[Only the names of files not containing selected 
lines are written to standard output]'
 {-l,--files-with-matches}'[Only the names of files containing selected lines 
are written to standard output]'
@@ -78,7 +78,7 @@
 --line-buffered'[Force output to be line buffered instead of block buffered]'
 --lines'[Boolean line matching mode for option --bool, the default mode]'
 '(1)*'{-M,--file-magic=-}'[Only search files matching the magic signature 
pattern MAGIC]:PATTERN:( )'
-{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop after MAX matches 
when specified]:MIN,MAX:( )::(1 1, 1,10)'
+{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop reading input upon 
MAX matches]:MIN,MAX:( )::(1 1, 1,10)'
 --match'[Match all lines]'
 --max-files=-'[Restrict the number of files matched to NUM]'
 --max-size=-'[Only search files whose physical size does not exceed MAX bytes]'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/completions/zsh/_ugrep+ 
new/ugrep-7.7.0/completions/zsh/_ugrep+
--- old/ugrep-7.6.0/completions/zsh/_ugrep+     2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/completions/zsh/_ugrep+     2026-04-21 19:29:26.000000000 
+0200
@@ -70,7 +70,7 @@
 {-J,--jobs=-}'[Specifies the number of threads spawned to search files]:NUM:( 
)'
 {-j,--smart-case}'[Perform case insensitive matching, unless a pattern is 
specified with a literal upper case letter]'
 --json'[Output file matches in JSON]'
-{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop at 
line MAX when specified]:MIN,MAX:( )'
+{-K,--range=-,--min-line=-,--max-line=-}'[Start searching at line MIN, stop 
reading input after line MAX]:MIN,MAX:( )'
 {-k,--column-number}'[The column number of a pattern match is displayed in 
front of the respective matched line, starting at column 1]'
 {-L,--files-without-match}'[Only the names of files not containing selected 
lines are written to standard output]'
 {-l,--files-with-matches}'[Only the names of files containing selected lines 
are written to standard output]'
@@ -78,7 +78,7 @@
 --line-buffered'[Force output to be line buffered instead of block buffered]'
 --lines'[Boolean line matching mode for option --bool, the default mode]'
 '(1)*'{-M,--file-magic=-}'[Only search files matching the magic signature 
pattern MAGIC]:PATTERN:( )'
-{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop after MAX matches 
when specified]:MIN,MAX:( )::(1 1, 1,10)'
+{-m,--min-count=-,--max-count=-}'[Require MIN matches, stop reading input upon 
MAX matches]:MIN,MAX:( )::(1 1, 1,10)'
 --match'[Match all lines]'
 --max-files=-'[Restrict the number of files matched to NUM]'
 --max-size=-'[Only search files whose physical size does not exceed MAX bytes]'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/configure new/ugrep-7.7.0/configure
--- old/ugrep-7.6.0/configure   2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/configure   2026-04-21 19:29:26.000000000 +0200
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for ugrep 7.6.0.
+# Generated by GNU Autoconf 2.72 for ugrep 7.7.0.
 #
 # Report bugs to <https://github.com/Genivia/ugrep/issues>.
 #
@@ -12,7 +12,7 @@
 # This configure script is free software; the Free Software Foundation
 # gives unlimited permission to copy, distribute and modify it.
 #
-# Copyright (C) 2019-2025 Robert van Engelen, Genivia Inc.
+# Copyright (C) 2019-2026 Robert van Engelen, Genivia Inc.
 ## -------------------- ##
 ## M4sh Initialization. ##
 ## -------------------- ##
@@ -606,8 +606,8 @@
 # Identity of this package.
 PACKAGE_NAME='ugrep'
 PACKAGE_TARNAME='ugrep'
-PACKAGE_VERSION='7.6.0'
-PACKAGE_STRING='ugrep 7.6.0'
+PACKAGE_VERSION='7.7.0'
+PACKAGE_STRING='ugrep 7.7.0'
 PACKAGE_BUGREPORT='https://github.com/Genivia/ugrep/issues'
 PACKAGE_URL='https://ugrep.com'
 
@@ -791,6 +791,7 @@
 enable_dependency_tracking
 enable_sse2
 enable_avx2
+enable_avx512
 enable_neon
 with_pcre2
 with_boost_regex
@@ -1382,7 +1383,7 @@
   # 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 7.6.0 to adapt to many kinds of systems.
+'configure' configures ugrep 7.7.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1453,7 +1454,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ugrep 7.6.0:";;
+     short | recursive ) echo "Configuration of ugrep 7.7.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1467,9 +1468,10 @@
                           do not reject slow dependency extractors
   --disable-dependency-tracking
                           speeds up one-time build
-  --disable-sse2          disable SSE2 CPU extensions
-  --disable-avx2          disable AVX2/AVX512BW CPU extensions
-  --disable-neon          disable NEON/AArch64 CPU extensions
+  --disable-sse2          disable all SIMD SSE2/AVX2/AVX512 CPU extensions
+  --disable-avx2          disable SIMD AVX2/AVX512BW CPU extensions, keep SSE2
+  --disable-avx512        disable SIMD AVX512BW CPU extensions, keep SSE2/AVX2
+  --disable-neon          disable SIMD NEON/AArch64 CPU extensions
   --disable-7zip          to disable 7zip and no longer search .7z files (7z
                           requires more memory and takes long to decompress)
   --enable-static         build static ugrep binaries
@@ -1630,14 +1632,14 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ugrep configure 7.6.0
+ugrep configure 7.7.0
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
 gives unlimited permission to copy, distribute and modify it.
 
-Copyright (C) 2019-2025 Robert van Engelen, Genivia Inc.
+Copyright (C) 2019-2026 Robert van Engelen, Genivia Inc.
 _ACEOF
   exit
 fi
@@ -2184,7 +2186,7 @@
 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 7.6.0, which was
+It was created by ugrep $as_me 7.7.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3694,7 +3696,7 @@
 
 # Define the identity of the package.
  PACKAGE='ugrep'
- VERSION='7.6.0'
+ VERSION='7.7.0'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -8853,7 +8855,7 @@
 # Use SIMD CPU extensions and manage cross compilation
 
################################################################################
 
-# --disable-sse2 fallback option if cross compilation fails to disable SSE2 
CPU extensions
+# --disable-sse2 fallback option if cross compilation fails, disables 
SSE2/AVX2/AVX512BW CPU extensions
 # Check whether --enable-sse2 was given.
 if test ${enable_sse2+y}
 then :
@@ -8864,7 +8866,7 @@
 fi
 
 
-# --disable-avx2 fallback option if cross compilation fails to disable 
AVX2/AVX512BW CPU extensions
+# --disable-avx2 fallback option if cross compilation fails, disables 
AVX2/AVX512BW CPU extensions
 # Check whether --enable-avx2 was given.
 if test ${enable_avx2+y}
 then :
@@ -8875,6 +8877,17 @@
 fi
 
 
+# --disable-avx512 fallback option if cross compilation fails, disables 
AVX512BW CPU extensions
+# Check whether --enable-avx512 was given.
+if test ${enable_avx512+y}
+then :
+  enableval=$enable_avx512; with_no_avx512="yes"
+else case e in #(
+  e) with_no_avx512="no" ;;
+esac
+fi
+
+
 # --disable-neon fallback option if cross compilation fails to disable 
NEON/AArch64 CPU extensions
 # Check whether --enable-neon was given.
 if test ${enable_neon+y}
@@ -8932,8 +8945,9 @@
             { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether 
${CXX} supports AVX2/AVX512BW intrinsics" >&5
 printf %s "checking whether ${CXX} supports AVX2/AVX512BW intrinsics... " >&6; 
}
             save_CXXFLAGS=$CXXFLAGS
-            CXXFLAGS="-mavx512bw"
-            cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+            if ! test "x$with_no_avx512" = "xyes"; then
+              CXXFLAGS="-mavx512bw"
+              cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <immintrin.h>
 int
@@ -8952,10 +8966,36 @@
 esac
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-            if test "x$mavx_ok" = "xyes"; then
-              SIMD_FLAGS="-msse2 -DHAVE_AVX512BW"
-              SIMD_AVX2_FLAGS="-mavx2"
-              SIMD_AVX512BW_FLAGS="-mavx512bw"
+              if test "x$mavx_ok" = "xyes"; then
+                SIMD_FLAGS="-msse2 -DHAVE_AVX512BW"
+                SIMD_AVX2_FLAGS="-mavx2"
+                SIMD_AVX512BW_FLAGS="-mavx512bw"
+              else
+                CXXFLAGS="-mavx2"
+                cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <immintrin.h>
+int
+main (void)
+{
+__m256i n = _mm256_set1_epi8(42); 
(void)_mm256_movemask_epi8(_mm256_and_si256(n, n));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+  mavx_ok=yes
+else case e in #(
+  e) mavx_ok=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+                if test "x$mavx_ok" = "xyes"; then
+                  SIMD_FLAGS="-msse2 -DHAVE_AVX2"
+                  SIMD_AVX2_FLAGS="-mavx2"
+                fi
+              fi
             else
               CXXFLAGS="-mavx2"
               cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -11835,7 +11875,7 @@
 # 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 7.6.0, which was
+This file was extended by ugrep $as_me 7.7.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -11908,7 +11948,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-ugrep config.status 7.6.0
+ugrep config.status 7.7.0
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/configure.ac new/ugrep-7.7.0/configure.ac
--- old/ugrep-7.6.0/configure.ac        2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/configure.ac        2026-04-21 19:29:26.000000000 +0200
@@ -1,7 +1,7 @@
-AC_INIT([ugrep],[7.6.0],[https://github.com/Genivia/ugrep/issues],[ugrep],[https://ugrep.com])
+AC_INIT([ugrep],[7.7.0],[https://github.com/Genivia/ugrep/issues],[ugrep],[https://ugrep.com])
 AM_INIT_AUTOMAKE([foreign subdir-objects dist-xz no-dist-gzip])
 AC_CONFIG_HEADERS([config.h])
-AC_COPYRIGHT([Copyright (C) 2019-2025 Robert van Engelen, Genivia Inc.])
+AC_COPYRIGHT([Copyright (C) 2019-2026 Robert van Engelen, Genivia Inc.])
 
 AC_CONFIG_MACRO_DIR([m4])
 
@@ -65,24 +65,31 @@
 # Use SIMD CPU extensions and manage cross compilation
 
################################################################################
 
-# --disable-sse2 fallback option if cross compilation fails to disable SSE2 
CPU extensions
+# --disable-sse2 fallback option if cross compilation fails, disables 
SSE2/AVX2/AVX512BW CPU extensions
 AC_ARG_ENABLE(sse2,
   [AS_HELP_STRING([--disable-sse2],
-                  [disable SSE2 CPU extensions])],
+                  [disable all SIMD SSE2/AVX2/AVX512 CPU extensions])],
   [with_no_sse2="yes"],
   [with_no_sse2="no"])
 
-# --disable-avx2 fallback option if cross compilation fails to disable 
AVX2/AVX512BW CPU extensions
+# --disable-avx2 fallback option if cross compilation fails, disables 
AVX2/AVX512BW CPU extensions
 AC_ARG_ENABLE(avx2,
   [AS_HELP_STRING([--disable-avx2],
-                  [disable AVX2/AVX512BW CPU extensions])],
+                  [disable SIMD AVX2/AVX512BW CPU extensions, keep SSE2])],
   [with_no_avx2="yes"],
   [with_no_avx2="no"])
 
+# --disable-avx512 fallback option if cross compilation fails, disables 
AVX512BW CPU extensions
+AC_ARG_ENABLE(avx512,
+  [AS_HELP_STRING([--disable-avx512],
+                  [disable SIMD AVX512BW CPU extensions, keep SSE2/AVX2])],
+  [with_no_avx512="yes"],
+  [with_no_avx512="no"])
+
 # --disable-neon fallback option if cross compilation fails to disable 
NEON/AArch64 CPU extensions
 AC_ARG_ENABLE(neon,
   [AS_HELP_STRING([--disable-neon],
-                 [disable NEON/AArch64 CPU extensions])],
+                 [disable SIMD NEON/AArch64 CPU extensions])],
   [with_no_neon="yes"],
   [with_no_neon="no"])
 
@@ -113,14 +120,25 @@
           if ! test "x$with_no_avx2" = "xyes"; then
             AC_MSG_CHECKING([whether ${CXX} supports AVX2/AVX512BW intrinsics])
             save_CXXFLAGS=$CXXFLAGS
-            CXXFLAGS="-mavx512bw"
-            AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <immintrin.h>]], 
[[__m512i n = _mm512_set1_epi8(42); (void)_mm512_cmpeq_epi8_mask(n, n);]])],
-                              [mavx_ok=yes],
-                              [mavx_ok=no])
-            if test "x$mavx_ok" = "xyes"; then
-              SIMD_FLAGS="-msse2 -DHAVE_AVX512BW"
-              SIMD_AVX2_FLAGS="-mavx2"
-              SIMD_AVX512BW_FLAGS="-mavx512bw"
+            if ! test "x$with_no_avx512" = "xyes"; then
+              CXXFLAGS="-mavx512bw"
+              AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <immintrin.h>]], 
[[__m512i n = _mm512_set1_epi8(42); (void)_mm512_cmpeq_epi8_mask(n, n);]])],
+                                [mavx_ok=yes],
+                                [mavx_ok=no])
+              if test "x$mavx_ok" = "xyes"; then
+                SIMD_FLAGS="-msse2 -DHAVE_AVX512BW"
+                SIMD_AVX2_FLAGS="-mavx2"
+                SIMD_AVX512BW_FLAGS="-mavx512bw"
+              else
+                CXXFLAGS="-mavx2"
+                AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <immintrin.h>]], 
[[__m256i n = _mm256_set1_epi8(42); 
(void)_mm256_movemask_epi8(_mm256_and_si256(n, n));]])],
+                                  [mavx_ok=yes],
+                                  [mavx_ok=no])
+                if test "x$mavx_ok" = "xyes"; then
+                  SIMD_FLAGS="-msse2 -DHAVE_AVX2"
+                  SIMD_AVX2_FLAGS="-mavx2"
+                fi
+              fi
             else
               CXXFLAGS="-mavx2"
               AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <immintrin.h>]], 
[[__m256i n = _mm256_set1_epi8(42); 
(void)_mm256_movemask_epi8(_mm256_and_si256(n, n));]])],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/include/reflex/input.h 
new/ugrep-7.7.0/include/reflex/input.h
--- old/ugrep-7.6.0/include/reflex/input.h      2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/include/reflex/input.h      2026-04-21 19:29:26.000000000 
+0200
@@ -632,7 +632,7 @@
       return static_cast<unsigned char>(c);
     return EOF;
   }
-  /// Copy character sequence data into buffer.
+  /// Read and copy input character sequence data into buffer.
   size_t get(
       char  *s, ///< points to the string buffer to fill with input
       size_t n) ///< size of buffer pointed to by s
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/lib/input.cpp 
new/ugrep-7.7.0/lib/input.cpp
--- old/ugrep-7.6.0/lib/input.cpp       2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/lib/input.cpp       2026-04-21 19:29:26.000000000 +0200
@@ -1033,6 +1033,7 @@
 #if (defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || 
defined(_WIN64) || defined(__BORLANDC__)) && !defined(__CYGWIN__) && 
!defined(__MINGW32__) && !defined(__MINGW64__)
   return !ferror(file_);
 #else
+  int fd = fileno(file_);
   if (ferror(file_))
   {
     if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
@@ -1042,7 +1043,7 @@
     if (errno == EAGAIN)
     {
       struct stat buf;
-      if (fstat(fileno(file_), &buf) == 0 && S_ISREG(buf.st_mode))
+      if (fstat(fd, &buf) == 0 && S_ISREG(buf.st_mode))
         return false;
     }
 #endif
@@ -1053,13 +1054,13 @@
     fd_set rfds, efds;
     FD_ZERO(&rfds);
     FD_ZERO(&efds);
-    FD_SET(0, &rfds);
-    FD_SET(0, &efds);
+    FD_SET(fd, &rfds);
+    FD_SET(fd, &efds);
     tv.tv_sec = 0;
-    tv.tv_usec = 1000; // 1ms timeout
-    clearerr(file_);   // unset EAGAIN etc
-    if (::select(fileno(file_) + 1, &rfds, NULL, &efds, &tv) >= 0)
-      return FD_ISSET(fileno(file_), &efds) == 0;
+    tv.tv_usec = 10000; // 10ms timeout
+    clearerr(file_);    // unset EAGAIN etc
+    if (::select(fd + 1, &rfds, NULL, &efds, &tv) >= 0)
+      return FD_ISSET(fd, &efds) == 0;
     if (errno != EINTR)
       return false;
   }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/lib/pattern.cpp 
new/ugrep-7.7.0/lib/pattern.cpp
--- old/ugrep-7.6.0/lib/pattern.cpp     2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/lib/pattern.cpp     2026-04-21 19:29:26.000000000 +0200
@@ -4423,7 +4423,7 @@
   // find min between 0 and Const::BITS
   min_ = Const::BITS;
   std::set<DFA::State*> prev, next(states);
-  for (size_t level = 0; level < min_; ++level)
+  for (uint16_t level = 0; level < min_; ++level)
   {
     bool none = true;
     prev.clear();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/lib/simd_avx2.cpp 
new/ugrep-7.7.0/lib/simd_avx2.cpp
--- old/ugrep-7.6.0/lib/simd_avx2.cpp   2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/lib/simd_avx2.cpp   2026-04-21 19:29:26.000000000 +0200
@@ -78,7 +78,7 @@
 #endif
 }
 
-// Partially check if valid UTF-8 encoding
+// Check if valid UTF-8 encoding and does not include a NUL, but pass 
surrogates and 3/4 byte overlongs
 bool simd_isutf8_avx2(const char *& b, const char *e)
 {
 #if defined(HAVE_AVX2) || defined(HAVE_AVX512BW)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/lzma/C/viizip.c 
new/ugrep-7.7.0/lzma/C/viizip.c
--- old/ugrep-7.6.0/lzma/C/viizip.c     2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/lzma/C/viizip.c     2026-04-21 19:29:26.000000000 +0200
@@ -250,6 +250,7 @@
   if (viizip == NULL)
     return;
 
+  ISzAlloc_Free(&viizip->alloc_main, viizip->look.buf);
   ISzAlloc_Free(&viizip->alloc_main, viizip->buf);
   SzFree(NULL, viizip->tmp);
   SzArEx_Free(&viizip->db, &viizip->alloc_main);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/man/ug.1 new/ugrep-7.7.0/man/ug.1
--- old/ugrep-7.6.0/man/ug.1    2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/man/ug.1    2026-04-21 19:29:26.000000000 +0200
@@ -1,4 +1,4 @@
-.TH UGREP "1" "March 05, 2026" "ugrep 7.6.0" "User Commands"
+.TH UGREP "1" "April 21, 2026" "ugrep 7.7.0" "User Commands"
 .SH NAME
 \fBugrep\fR, \fBug\fR -- file pattern searcher
 .SH SYNOPSIS
@@ -602,7 +602,7 @@
 additional values are output.  See also options \fB\-\-format\fR and \fB\-u\fR.
 .TP
 \fB\-K\fR [\fIMIN\fR,][\fIMAX\fR], \fB\-\-range\fR=[\fIMIN\fR,][\fIMAX\fR], 
\fB\-\-min\-line\fR=\fIMIN\fR, \fB\-\-max\-line\fR=\fIMAX\fR
-Start searching at line MIN, stop at line MAX when specified.
+Start searching at line MIN, stop reading input after line MAX.
 .TP
 \fB\-k\fR, \fB\-\-column\-number\fR
 The column number of a pattern match is displayed in front of the
@@ -617,10 +617,9 @@
 .TP
 \fB\-l\fR, \fB\-\-files\-with\-matches\fR
 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 input is
-searched, the string ``(standard input)'' is written.
+standard output.  Pathnames are listed once per file searched.  If
+the standard input is searched, the string ``(standard input)'' is
+written.  Stop reading input upon the first match.
 .TP
 \fB\-\-label\fR=\fILABEL\fR
 Displays the LABEL value when input is read from standard input
@@ -644,7 +643,7 @@
 making recursive searches potentially more expensive.
 .TP
 \fB\-m\fR [\fIMIN\fR,][\fIMAX\fR], \fB\-\-min\-count\fR=\fIMIN\fR, 
\fB\-\-max\-count\fR=\fIMAX\fR
-Require MIN matches, stop after MAX matches when specified.  Output
+Require MIN matches, stop reading input upon MAX matches.  Output
 MIN to MAX matches.  For example, \fB\-m\fR1 outputs the first match and
 \fB\-c\fRm1, (with a comma) counts nonzero matches.  When \fB\-u\fR or 
\fB\-\-ungroup\fR
 is specified, each individual match counts.  See also option \fB\-K\fR.
@@ -971,8 +970,7 @@
 xz (.xz, .txz) and lzma (requires suffix .lzma, .tlz),
 zstd (.zst, .zstd, .tzst),
 lz4 (requires suffix .lz4),
-brotli (requires suffix .br),
-bzip3 (requires suffix .bz3).
+brotli (requires suffix .br).
 .TP
 \fB\-\-zmax\fR=\fINUM\fR
 When used with option \fB\-z\fR or \fB\-\-decompress\fR, searches the contents 
of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/man/ugrep-indexer.1 
new/ugrep-7.7.0/man/ugrep-indexer.1
--- old/ugrep-7.6.0/man/ugrep-indexer.1 2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/man/ugrep-indexer.1 2026-04-21 19:29:26.000000000 +0200
@@ -1,4 +1,4 @@
-.TH UGREP-INDEXER "1" "March 05, 2026" "ugrep-indexer 7.6.0" "User Commands"
+.TH UGREP-INDEXER "1" "April 21, 2026" "ugrep-indexer 7.7.0" "User Commands"
 .SH NAME
 \fBugrep-indexer\fR -- file indexer to accelerate recursive searching
 .SH SYNOPSIS
@@ -147,8 +147,7 @@
 lzma and xz (requires suffix .lzma, .tlz, .xz, .txz),
 lz4 (requires suffix .lz4),
 zstd (requires suffix .zst, .zstd, .tzst),
-brotli (requires suffix .br),
-bzip3 (requires suffix .bz3).
+brotli (requires suffix .br).
 .TP
 \fB\-\-zmax\fR=\fINUM\fR
 When used with option \fB\-z\fR (\fB\-\-decompress\fR), indexes the contents of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/man/ugrep.1 new/ugrep-7.7.0/man/ugrep.1
--- old/ugrep-7.6.0/man/ugrep.1 2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/man/ugrep.1 2026-04-21 19:29:26.000000000 +0200
@@ -1,4 +1,4 @@
-.TH UGREP "1" "March 05, 2026" "ugrep 7.6.0" "User Commands"
+.TH UGREP "1" "April 21, 2026" "ugrep 7.7.0" "User Commands"
 .SH NAME
 \fBugrep\fR, \fBug\fR -- file pattern searcher
 .SH SYNOPSIS
@@ -602,7 +602,7 @@
 additional values are output.  See also options \fB\-\-format\fR and \fB\-u\fR.
 .TP
 \fB\-K\fR [\fIMIN\fR,][\fIMAX\fR], \fB\-\-range\fR=[\fIMIN\fR,][\fIMAX\fR], 
\fB\-\-min\-line\fR=\fIMIN\fR, \fB\-\-max\-line\fR=\fIMAX\fR
-Start searching at line MIN, stop at line MAX when specified.
+Start searching at line MIN, stop reading input after line MAX.
 .TP
 \fB\-k\fR, \fB\-\-column\-number\fR
 The column number of a pattern match is displayed in front of the
@@ -617,10 +617,9 @@
 .TP
 \fB\-l\fR, \fB\-\-files\-with\-matches\fR
 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 input is
-searched, the string ``(standard input)'' is written.
+standard output.  Pathnames are listed once per file searched.  If
+the standard input is searched, the string ``(standard input)'' is
+written.  Stop reading input upon the first match.
 .TP
 \fB\-\-label\fR=\fILABEL\fR
 Displays the LABEL value when input is read from standard input
@@ -644,7 +643,7 @@
 making recursive searches potentially more expensive.
 .TP
 \fB\-m\fR [\fIMIN\fR,][\fIMAX\fR], \fB\-\-min\-count\fR=\fIMIN\fR, 
\fB\-\-max\-count\fR=\fIMAX\fR
-Require MIN matches, stop after MAX matches when specified.  Output
+Require MIN matches, stop reading input upon MAX matches.  Output
 MIN to MAX matches.  For example, \fB\-m\fR1 outputs the first match and
 \fB\-c\fRm1, (with a comma) counts nonzero matches.  When \fB\-u\fR or 
\fB\-\-ungroup\fR
 is specified, each individual match counts.  See also option \fB\-K\fR.
@@ -971,8 +970,7 @@
 xz (.xz, .txz) and lzma (requires suffix .lzma, .tlz),
 zstd (.zst, .zstd, .tzst),
 lz4 (requires suffix .lz4),
-brotli (requires suffix .br),
-bzip3 (requires suffix .bz3).
+brotli (requires suffix .br).
 .TP
 \fB\-\-zmax\fR=\fINUM\fR
 When used with option \fB\-z\fR or \fB\-\-decompress\fR, searches the contents 
of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/output.cpp 
new/ugrep-7.7.0/src/output.cpp
--- old/ugrep-7.6.0/src/output.cpp      2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/src/output.cpp      2026-04-21 19:29:26.000000000 +0200
@@ -256,7 +256,7 @@
   size_t columno = flag_column_number && matcher != NULL ? matcher->columno() 
+ 1 : 1;
 
   // -Q: mark pathname with three \0 markers in headings, unless -a
-  bool nul = heading && flag_query && !flag_text;
+  bool nul = flag_query && heading && !flag_text;
 
   if (nul)
     chr('\0');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/query.cpp 
new/ugrep-7.7.0/src/query.cpp
--- old/ugrep-7.6.0/src/query.cpp       2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/src/query.cpp       2026-04-21 19:29:26.000000000 +0200
@@ -581,7 +581,7 @@
 void Query::disp(int row)
 {
   Screen::normal();
-  if (row < rows_)
+  if (row < rows_ + append_)
   {
     if (selected_[row])
       Screen::select();
@@ -683,7 +683,7 @@
   }
   else
   {
-    // do not show dots
+    // reset to 4 to do not show dots
     tick_ = 4;
 
     if (error_ == -1)
@@ -698,7 +698,7 @@
       if (row_ < 0)
         row_ = 0;
 
-      int end = row_ + maxrows_ - 2;
+      int end = row_ + append_ + maxrows_ - 2;
 
       for (int i = row_; i < end; ++i)
         disp(i);
@@ -851,13 +851,9 @@
 #else
 
   signal(SIGINT, sigint);
-
   signal(SIGQUIT, sigint);
-
   signal(SIGTERM, sigint);
-
   signal(SIGPIPE, SIG_IGN);
-
   signal(SIGWINCH, sigwinch);
 
 #endif
@@ -890,7 +886,7 @@
   VKey::cleanup();
   Screen::cleanup();
 
-  // check TTY again for color support, this time without --query
+  // check TTY a final time for color support to print results, if any, this 
time without --query
   flag_query = false;
   terminal();
 
@@ -909,16 +905,14 @@
 
   // close the stdin pipe
   if (flag_stdin && Static::source != stdin && Static::source != NULL)
-  {
-    fclose(Static::source);
-    Static::source = NULL;
-  }
+    fclose(Static::source); // close source but don't reset to NULL, source is 
used by the search thread
 
   // join the search thread
   if (search_thread_.joinable())
     search_thread_.join();
 
   // join the stdin sender thread
+  stdin_stop = true;
   if (stdin_thread_.joinable())
     stdin_thread_.join();
 }
@@ -1482,6 +1476,8 @@
 // start a new search, cancel the previous search when still running
 void Query::search()
 {
+  stop_stdin();
+
   bool cancel = !eof_;
 
   if (cancel)
@@ -1494,6 +1490,22 @@
     Static::cancel_ugrep();
   }
 
+  if (search_thread_.joinable())
+  {
+    if (cancel && error_ == -1)
+    {
+      const int banlen = 256;
+      char banner[banlen];
+      snprintf(banner, banlen, "restarting: please be patient while I cancel 
searching large files...%*s", banlen - 70, "");
+      Screen::normal();
+      Screen::invert();
+      Screen::put(maxrows_ - 1, 0, banner);
+      Screen::normal();
+    }
+
+    search_thread_.join();
+  }
+
 #ifdef OS_WIN
 
   hPipe_ = nonblocking_pipe(search_pipe_);
@@ -1522,23 +1534,8 @@
 
 #endif
 
-  if (search_thread_.joinable())
-  {
-    if (cancel && error_ == -1)
-    {
-      const int banlen = 256;
-      char banner[banlen];
-      snprintf(banner, banlen, "restarting: please be patient while I cancel 
searching large files...%*s", banlen - 70, "");
-      Screen::normal();
-      Screen::invert();
-      Screen::put(maxrows_ - 1, 0, banner);
-      Screen::normal();
-    }
-
-    search_thread_.join();
-  }
-
   eof_ = false;
+  append_ = false;
   row_ = 0;
   rows_ = 0;
   skip_ = 0;
@@ -1568,7 +1565,7 @@
 
   set_flags();
 
-  set_stdin();
+  start_stdin();
 
   if (error_ == -1)
   {
@@ -1667,32 +1664,29 @@
   // fetch viewable portion plus one up to two screenfuls more
   fetch(row_ - (row_ % Screen::rows) + 2 * Screen::rows);
 
+  int end = rows_ + append_;
+
   // display the viewable portion when updated
-  if (rows_ > begin && begin < row_ + maxrows_ - 2)
+  if (end > begin && begin < row_ + maxrows_ - 2)
   {
     Screen::normal();
 
-    if (begin + maxrows_ - 2 > rows_)
-      begin = rows_ - maxrows_ + 2;
+    if (begin + maxrows_ - 2 > end)
+      begin = end - maxrows_ + 2;
     if (begin < 0)
       begin = 0;
     if (begin < row_)
       begin = row_;
 
-    int end = rows_;
-
-    if (end > row_ + maxrows_ - 2)
-      end = row_ + maxrows_ - 2;
-
-    for (int i = begin; i < end; ++i)
+    for (int i = begin; i < std::min(end, row_ + maxrows_ - 2); ++i)
       disp(i);
   }
 
   if (error_ == -1)
   {
-    if (tick_ < 8 && rows_ < row_ + maxrows_ - 2)
+    if (tick_ < 8 && end < row_ + maxrows_ - 2)
     {
-      int row = rows_ - row_ + 1;
+      int row = end - row_ + 1;
 
       // no dots and clear below when we hit eof
       if (eof_)
@@ -1913,7 +1907,8 @@
       if (!incomplete)
       {
         // added another row
-        ++rows_;
+        if (++rows_ < static_cast<int>(view_.size()))
+          view_[rows_].clear();
 
         // skip \n
         if (nlptr < buffer_ + buflen_)
@@ -1923,9 +1918,8 @@
       // append the next chunk of text from the buffer
       append_ = incomplete;
 
+      // shift the buffer to make room
       buflen_ -= nlptr - buffer_;
-
-      // shift the buffer
       memmove(buffer_, nlptr, buflen_);
     }
   }
@@ -2488,13 +2482,6 @@
 
     if (found)
     {
-
-      Screen::clear();
-      Screen::put("Waiting on ");
-      Screen::put(command.c_str());
-      Screen::put(" to finish");
-      Screen::home();
-
       // -n: add +line_number
       if (line_number > 0)
         command.append(" +").append(std::to_string(line_number));
@@ -2534,23 +2521,44 @@
           catch (...)
           {
             // this should never happen, but just in case we ignore errors
+            Screen::alert();
+            return;
           }
         }
       }
       else
       {
-        // view file in the pager using system() call
-        command.append(" \"").append(filename).append("\"");
-
 #ifdef OS_WIN
+        if (filename.empty() || filename.at(0) == '/' || filename.find('"') != 
std::string::npos)
+        {
+          // illegal filename in Windows, should never happen, but just in case
+          Screen::alert();
+          return;
+        }
+        // view file in the pager using Windows _wsystem() call
+        command.append(" \"").append(filename).append("\"");
         // Windows system() does not support non-ASCII, instead we use a wide 
string with _wsystem()
         wcommand = utf8_decode(command);
-
         // flush before calling _wsystem(), according to the Window's system 
API documentation
         // _flushall(); removed because this may cause the TUI to freeze
+#else
+        if (filename.find('\'') != std::string::npos)
+        {
+          // incorrect filename
+          Screen::alert();
+          return;
+        }
+        // view file in the pager using system() call, double -- ends options
+        command.append(" -- '").append(filename).append("'");
 #endif
       }
 
+      Screen::clear();
+      Screen::put("Waiting on ");
+      Screen::put(command.c_str());
+      Screen::put(" to finish");
+      Screen::home();
+
       // pipe to pager was OK or executing the command is OK
       bool ok;
 
@@ -4112,37 +4120,26 @@
 
 void Query::get_stdin()
 {
-  // if standard input is searched, then buffer all its text
+  // if standard input is searched, then dynamically buffer the input to search
   if (flag_stdin)
   {
-    reflex::BufferedInput input(stdin, flag_encoding_type);
+#ifndef OS_WIN
+    // make stdin non-blocking to read and buffer dynamically
+    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
+#endif
 
-    while (true)
-    {
-      size_t len = input.get(buffer_, QUERY_BUFFER_SIZE);
-      if (len == 0)
-        break;
-      stdin_buffer_.append(buffer_, len);
-    }
+    stdin_input_ = stdin;
+    stdin_input_.file_encoding(flag_encoding_type);
+    stdin_buffer_.clear();
   }
 }
 
-void Query::set_stdin()
+// start a new stdin sender thread and pipe from sender to the search engine
+void Query::start_stdin()
 {
   // if standard input is searched, start thread to reproduce its text on 
demand
   if (flag_stdin)
   {
-    // close the stdin pipe when open
-    if (Static::source != stdin && Static::source != NULL)
-    {
-      fclose(Static::source);
-      Static::source = NULL;
-    }
-
-    // join the stdin_sender
-    if (stdin_thread_.joinable())
-      stdin_thread_.join();
-
     // create a new pipe
     if (pipe(stdin_pipe_) < 0)
     {
@@ -4156,19 +4153,68 @@
     Static::source = fdopen(stdin_pipe_[0], "rb");
 
     // run stdin_sender in the background to push buffered standard input down 
the pipe
+    stdin_stop = false;
     stdin_thread_ = std::thread(Query::stdin_sender, stdin_pipe_[1]);
   }
 }
 
+// stop stdin sender thread
+void Query::stop_stdin()
+{
+  if (flag_stdin)
+  {
+    // close the stdin pipe when open
+    if (Static::source != stdin && Static::source != NULL)
+      fclose(Static::source); // close source but don't reset to NULL, source 
is used by the search thread
+
+    // join the stdin_sender
+    stdin_stop = true;
+    if (stdin_thread_.joinable())
+      stdin_thread_.join();
+  }
+}
+
 // push standard input down the specified pipe fd
-ssize_t Query::stdin_sender(int fd)
+void Query::stdin_sender(int fd)
 {
-  // write the stdin data all at once, we can ignore the return value
-  ssize_t nwritten = write(fd, stdin_buffer_.c_str(), stdin_buffer_.size());
+  // write the stdin data all at once to the search pipe
+  char buf[QUERY_BUFFER_SIZE];
+  ssize_t len = stdin_buffer_.size(), nwritten = write(fd, 
stdin_buffer_.c_str(), len);
 
-  close(fd);
+  // when more stdin input arrives, buffer it and write it to the search pipe
+  while (!stdin_stop && !feof(stdin) && nwritten == len)
+  {
+    len = stdin_input_.file_get(buf, QUERY_BUFFER_SIZE);
+
+    if (len == 0)
+    {
+      if (feof(stdin))
+        break;
+
+#ifndef OS_WIN
+      struct timeval tv;
+      fd_set rfds, efds;
+      FD_ZERO(&rfds);
+      FD_ZERO(&efds);
+      FD_SET(0, &rfds);
+      FD_SET(0, &efds);
+      tv.tv_sec = 0;
+      tv.tv_usec = 10000; // 10ms timeout
+      clearerr(stdin);
+      if (::select(0 + 1, &rfds, NULL, &efds, &tv) >= 0 && FD_ISSET(0, &efds) 
!= 0)
+        break;
+#endif
 
-  return nwritten;
+      nwritten = 0;
+    }
+    else
+    {
+      stdin_buffer_.append(buf, len);
+      nwritten = write(fd, buf, len);
+    }
+  }
+
+  close(fd);
 }
 
 // true if view_[ref] has a valid filename/filepath identified by three \0 
markers and differs from the given filename, then assigns filename
@@ -4413,9 +4459,11 @@
 char                       Query::buffer_[QUERY_BUFFER_SIZE];
 int                        Query::search_pipe_[2];
 std::thread                Query::search_thread_;
+reflex::Input              Query::stdin_input_;
 std::string                Query::stdin_buffer_;
 int                        Query::stdin_pipe_[2];
 std::thread                Query::stdin_thread_;
+std::atomic_bool           Query::stdin_stop;
 size_t                     Query::searched_            = 0;
 size_t                     Query::found_               = 0;
 int                        Query::tick_                = 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/query.hpp 
new/ugrep-7.7.0/src/query.hpp
--- old/ugrep-7.6.0/src/query.hpp       2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/src/query.hpp       2026-04-21 19:29:26.000000000 +0200
@@ -263,9 +263,10 @@
 
   static void get_stdin();
 
-  static void set_stdin();
+  static void start_stdin();
+  static void stop_stdin();
 
-  static ssize_t stdin_sender(int fd);
+  static void stdin_sender(int fd);
 
   static bool find_filename(int ref, std::string& filename, bool compare_dir = 
false, bool find_path = false, std::string *partname = NULL);
 
@@ -310,14 +311,16 @@
   static std::vector<std::string> view_;        // search output text to 
display, incrementally fetched
   static std::vector<bool>        selected_;    // marked lines in view_[] 
selected in selection mode
   static bool                     eof_;         // end of results, no more 
results can be fetched
-  static bool                     append_;
+  static bool                     append_;      // flag to append search 
output to the last incomplete line
   static size_t                   buflen_;
   static char                     buffer_[QUERY_BUFFER_SIZE];
   static int                      search_pipe_[2];
   static std::thread              search_thread_;
+  static reflex::Input            stdin_input_;
   static std::string              stdin_buffer_;
   static int                      stdin_pipe_[2];
   static std::thread              stdin_thread_;
+  static std::atomic_bool         stdin_stop;
   static size_t                   searched_;    // last update number of files 
searched
   static size_t                   found_;       // last update number of files 
found
   static int                      tick_;        // 100ms tick 0 to 7 or steady 
8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/stats.cpp 
new/ugrep-7.7.0/src/stats.cpp
--- old/ugrep-7.6.0/src/stats.cpp       2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/src/stats.cpp       2026-04-21 19:29:26.000000000 +0200
@@ -43,6 +43,10 @@
 {
   size_t sf = searched_files();
   size_t sd = searched_dirs();
+  size_t si = searched_indexed();
+  size_t ss = searched_skipped();
+  size_t sc = searched_changed();
+  size_t sa = searched_added();
   size_t sl = searched_lines();
   size_t ff = found_files();
   size_t fp = found_parts();
@@ -71,17 +75,17 @@
 
   if (flag_index && indexed > 0)
   {
-    fprintf(output, "Skipped %zu file%s of %zu (%.4g%%) not matching %zu 
indexes\n", skipped, (skipped == 1 ? "" : "s"), sf - ff, 100.0 * skipped / (sf 
- ff), indexed);
-    if (changed > 0 || added > 0)
+    fprintf(output, "Skipped %zu file%s of %zu (%.4g%%) not matching %zu 
indexes\n", ss, (ss == 1 ? "" : "s"), sf - ff, 100.0 * ss / (sf - ff), si);
+    if (sc > 0 || sa > 0)
     {
       fprintf(output, "Detected outdated or missing index files, run 
ugrep-indexer to re-index:\n");
-      if (changed > 1)
-        fprintf(output, "  searched %zu changed files\n", changed);
-      else if (changed == 1)
+      if (sc > 1)
+        fprintf(output, "  searched %zu changed files\n", sc);
+      else if (sc == 1)
         fprintf(output, "  searched 1 changed file\n");
-      if (added > 1)
-        fprintf(output, "  searched %zu new files\n", added);
-      else if (added == 1)
+      if (sa > 1)
+        fprintf(output, "  searched %zu new files\n", sa);
+      else if (sa == 1)
         fprintf(output, "  searched 1 new file\n");
     }
   }
@@ -200,12 +204,12 @@
 }
 
 reflex::timer_type       Stats::timer;
-size_t                   Stats::files   = 0;
-size_t                   Stats::dirs    = 0;
-size_t                   Stats::indexed = 0;
-size_t                   Stats::skipped = 0;
-size_t                   Stats::changed = 0;
-size_t                   Stats::added   = 0;
+std::atomic_size_t       Stats::files;
+std::atomic_size_t       Stats::dirs;
+std::atomic_size_t       Stats::indexed;
+std::atomic_size_t       Stats::skipped;
+std::atomic_size_t       Stats::changed;
+std::atomic_size_t       Stats::added;
 std::atomic_size_t       Stats::fileno;
 std::atomic_size_t       Stats::partno;
 std::atomic_size_t       Stats::matchno;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/stats.hpp 
new/ugrep-7.7.0/src/stats.hpp
--- old/ugrep-7.6.0/src/stats.hpp       2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/src/stats.hpp       2026-04-21 19:29:26.000000000 +0200
@@ -126,6 +126,30 @@
     return dirs;
   }
 
+  // number of indexed files visited
+  static size_t searched_indexed()
+  {
+    return indexed;
+  }
+
+  // numbder of indexed files skipped from search
+  static size_t searched_skipped()
+  {
+    return skipped;
+  }
+
+  // number of indexed files changed and searched (stale index file)
+  static size_t searched_changed()
+  {
+    return changed;
+  }
+
+  // number of non-indexed files added and searched (stale index file)
+  static size_t searched_added()
+  {
+    return added;
+  }
+
   // number of lines searched
   static size_t searched_lines()
   {
@@ -190,12 +214,12 @@
  protected:
 
   static reflex::timer_type       timer;   // elapsed wall-clock time in milli 
seconds (ms)
-  static size_t                   files;   // number of files searched, 
excluding files in archives
-  static size_t                   dirs;    // number of directories searched
-  static size_t                   indexed; // number of files found to be 
indexed
-  static size_t                   skipped; // number of files found to be 
indexed that were skipped as not matching
-  static size_t                   changed; // number of files found to be 
indexed but changed (stale index file)
-  static size_t                   added;   // number of files found to be 
added (stale index file)
+  static std::atomic_size_t       files;   // number of files searched, 
excluding files in archives
+  static std::atomic_size_t       dirs;    // number of directories searched
+  static std::atomic_size_t       indexed; // number of files found to be 
indexed
+  static std::atomic_size_t       skipped; // number of files found to be 
indexed that were skipped as not matching
+  static std::atomic_size_t       changed; // number of files found to be 
indexed but changed (stale index file)
+  static std::atomic_size_t       added;   // number of files found to be 
added (stale index file)
   static std::atomic_size_t       fileno;  // number of matching files, 
excluding files in archives, atomic for GrepWorker::search() update
   static std::atomic_size_t       partno;  // number of matching files, 
including files in archives, atomic for GrepWorker::search() update
   static std::atomic_size_t       lineno;  // number of lines searched 
cummulatively
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/ugrep-indexer.cpp 
new/ugrep-7.7.0/src/ugrep-indexer.cpp
--- old/ugrep-7.6.0/src/ugrep-indexer.cpp       2026-03-05 20:19:48.000000000 
+0100
+++ new/ugrep-7.7.0/src/ugrep-indexer.cpp       2026-04-21 19:29:26.000000000 
+0200
@@ -35,7 +35,7 @@
 */
 
 // DO NOT ALTER THIS LINE: updated by makemake.sh and we need it physically 
here for MSVC++ build from source
-#define UGREP_VERSION "7.6.0"
+#define UGREP_VERSION "7.7.0"
 
 // use a task-parallel thread to decompress the stream into a pipe to search, 
also handles nested archives
 #define WITH_DECOMPRESSION_THREAD
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/ugrep.cpp 
new/ugrep-7.7.0/src/ugrep.cpp
--- old/ugrep-7.6.0/src/ugrep.cpp       2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/src/ugrep.cpp       2026-04-21 19:29:26.000000000 +0200
@@ -132,6 +132,8 @@
 #include <stringapiset.h>       // internationalization
 #include <direct.h>             // directory access
 #include <winsock.h>            // gethostname() for --hyperlink
+#include <securitybaseapi.h>    // GetTokenInformation()
+#include <aclapi.h>             // GetSecurityInfo()
 #pragma comment(lib, "Ws2_32.lib")
 
 #else
@@ -2269,6 +2271,26 @@
 
 #endif
 
+  // extend the reflex::Input::Handler to handle detection of binary input 
like GNU grep (check for NULs)
+  struct BinaryHandler : public reflex::Input::Handler {
+
+    BinaryHandler(Grep& grep)
+      :
+        grep(grep)
+    { }
+
+    Grep& grep;
+
+    size_t operator()(FILE*, char *buf, size_t len)
+    {
+      // simpler form of binary file detection without Unicode UTF-8 
validation, check for NULs like GNU grep
+      grep.binfile = grep.binfile || (memchr(buf, 0, len) != NULL);
+
+      return len;
+    }
+
+  };
+
 #ifndef OS_WIN
 
   // extend the reflex::Input::Handler to handle non-blocking input from a 
character device (TTY) or from a pipe
@@ -2295,7 +2317,7 @@
       }
       else if (!flag_text && !flag_hex && !flag_with_hex && flag_encoding_type 
!= reflex::Input::file_encoding::null_data && !grep.binfile)
       {
-        // simpler form of binary file detection without Unicode UTF-8 
validation, like GNU grep
+        // simpler form of binary file detection without Unicode UTF-8 
validation, check for NULs like GNU grep
         grep.binfile = (memchr(buf, 0, len) != NULL);
       }
 
@@ -3211,7 +3233,8 @@
       out(file),
       matcher(matcher),
       matchers(matchers),
-      file_in(NULL)
+      file_in(NULL),
+      bin_handler(*this)
 #ifndef OS_WIN
     , stdin_handler(*this)
 #endif
@@ -3897,8 +3920,9 @@
 
 #if defined(WITH_STDIN_DRAIN) && !defined(OS_WIN)
 
-    // drain stdin when non-seekable file such as a pipe until eof
-    if (file_in == stdin && !feof(stdin) && fseek(stdin, 0, SEEK_END) < 0 && 
errno != EINVAL)
+    // drain stdin when non-seekable file such as a pipe until eof, unless 
option -q is used
+    if (file_in == stdin && !flag_quiet && !flag_files_with_matches && 
flag_max_count == 0 && flag_max_line == 0 &&
+        !feof(stdin) && fseek(stdin, 0, SEEK_END) < 0 && errno != EINVAL)
     {
       char buf[16384];
       while (input.get(buf, sizeof(buf)) > 0)
@@ -3961,19 +3985,23 @@
 #endif
     }
 
-    // when non-blocking do not check init_is_binary(), wait until handler 
detects binary files that have NUL bytes
+    // interactive: do not check init_is_binary(), wait until handler detects 
binary files that have NUL bytes
     if (!interactive)
     {
+      // otherwise, check if the file is a binary file
       if (flag_binary_without_match)
       {
         // -I: do not match binary files
         if (init_is_binary())
           return false;
       }
-      else if (!flag_quiet && !flag_files_with_matches && !flag_count && 
flag_format == NULL && !flag_text && !flag_hex && !flag_with_hex)
+      else if (!flag_quiet && !flag_files_with_matches && !flag_count && 
flag_format == NULL && !flag_text && !flag_hex && !flag_with_hex && 
flag_encoding_type != reflex::Input::file_encoding::null_data)
       {
-        // not -q, -l, -c, --format, -a -X, -W: check if initial part of the 
file is binary
+        // not -q, -l, -c, --format, -a, -X, -W, -00: check if initial part of 
the file is binary
         binfile = init_is_binary();
+
+        // detect binary input dynamically when more input is read after 
checking the inital input
+        matcher->in.set_handler(&bin_handler);
       }
     }
 
@@ -4089,6 +4117,7 @@
   bool                           binary;        // the match is binary as per 
option -X or -W
   size_t                         matches;       // number of matches
   bool                           stop;          // stop searching when 
--max-files max reached
+  BinaryHandler                  bin_handler;   // a handler to detect binary 
input like GNU grep
 #ifndef OS_WIN
   StdInHandler                   stdin_handler; // a handler to handle 
non-blocking input from a TTY or a slow pipe
 #endif
@@ -4779,10 +4808,59 @@
   std::string config_file(flag_config);
   FILE *file = NULL;
 
-  if (home || fopen_smart(&file, flag_config, "r") != 0)
+  if (!home)
   {
-    file = NULL;
+    // try opening a config file in the working directory
+    if (fopen_smart(&file, flag_config, "r") == 0)
+    {
+      if (file != NULL && file != stdin)
+      {
+        // check if we own this config file located in the working directory
+#ifdef OS_WIN
+        HANDLE hFile = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(file)));
+        PSID pSidOwner = NULL;
+        PSECURITY_DESCRIPTOR pSD = NULL;
+        if (GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, 
&pSidOwner, NULL, NULL, NULL, &pSD) == ERROR_SUCCESS)
+        {
+          HANDLE hToken = NULL;
+          if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
+          {
+            DWORD dwBufferSize = 0;
+            GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize);
+            PTOKEN_USER pTokenUser = 
reinterpret_cast<PTOKEN_USER>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 
dwBufferSize));
+            if (pTokenUser != NULL)
+            {
+              if (!GetTokenInformation(hToken, TokenUser, pTokenUser, 
dwBufferSize, &dwBufferSize) ||
+                  !EqualSid(pSidOwner, pTokenUser->User.Sid))
+              {
+                fclose(file);
+                file = NULL;
+              }
+              HeapFree(GetProcessHeap(), 0, pTokenUser);
+            }
+            CloseHandle(hToken);
+          }
+          if (pSD)
+            LocalFree(pSD);
+        }
+#else
+        struct stat buf;
+        if (fstat(fileno(file), &buf) == 0 && buf.st_uid != getuid())
+        {
+          fclose(file);
+          file = NULL;
+        }
+#endif
+      }
+    }
+    else
+    {
+      file = NULL;
+    }
+  }
 
+  if (file == NULL)
+  {
     // if not in the working directory, then check the home directory
     if (Static::home_dir != NULL && *flag_config != '~' && *flag_config != 
PATHSEPCHR)
     {
@@ -14369,7 +14447,7 @@
             Output file matches in JSON.  When -H, -n, -k, or -b is 
specified,\n\
             additional values are output.  See also options --format and -u.\n\
     -K [MIN,][MAX], --range=[MIN,][MAX], --min-line=MIN, --max-line=MAX\n\
-            Start searching at line MIN, stop at line MAX when specified.\n\
+            Start searching at line MIN, stop reading input after line MAX.\n\
     -k, --column-number\n\
             The column number of a pattern match is displayed in front of 
the\n\
             respective matched line, starting at column 1.  Tabs are 
expanded\n\
@@ -14381,10 +14459,9 @@
             is written.\n\
     -l, --files-with-matches\n\
             Only the names of files containing selected lines are written to\n\
-            standard output.  ugrep will only search a file until a match 
has\n\
-            been found, making searches potentially less expensive.  
Pathnames\n\
-            are listed once per file searched.  If the standard input is\n\
-            searched, the string ``(standard input)'' is written.\n\
+            standard output.  Pathnames are listed once per file searched.  
If\n\
+            the standard input is searched, the string ``(standard input)'' 
is\n\
+            written.  Stop reading input upon the first match.\n\
     --label=LABEL\n\
             Displays the LABEL value when input is read from standard input\n\
             where a file name would normally be printed in the output.\n\
@@ -14403,7 +14480,7 @@
             with options -O and -t.  Every file on the search path is read,\n\
             making recursive searches potentially more expensive.\n\
     -m [MIN,][MAX], --min-count=MIN, --max-count=MAX\n\
-            Require MIN matches, stop after MAX matches when specified.  
Output\n\
+            Require MIN matches, stop reading input upon MAX matches.  
Output\n\
             MIN to MAX matches.  For example, -m1 outputs the first match 
and\n\
             -cm1, (with a comma) counts nonzero matches.  When -u or 
--ungroup\n\
             is specified, each individual match counts.  See also option -K.\n\
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-7.6.0/src/ugrep.hpp 
new/ugrep-7.7.0/src/ugrep.hpp
--- old/ugrep-7.6.0/src/ugrep.hpp       2026-03-05 20:19:48.000000000 +0100
+++ new/ugrep-7.7.0/src/ugrep.hpp       2026-04-21 19:29:26.000000000 +0200
@@ -38,7 +38,7 @@
 #define UGREP_HPP
 
 // DO NOT ALTER THIS LINE: updated by makemake.sh and we need it physically 
here for MSVC++ build from source
-#define UGREP_VERSION "7.6.0"
+#define UGREP_VERSION "7.7.0"
 
 // disable mmap because mmap is almost always slower than the file reading 
speed improvements since 3.0.0
 #define WITH_NO_MMAP

++++++ ugrep.obsinfo ++++++
--- /var/tmp/diff_new_pack.urNPdn/_old  2026-04-22 17:02:43.935760176 +0200
+++ /var/tmp/diff_new_pack.urNPdn/_new  2026-04-22 17:02:43.967761455 +0200
@@ -1,5 +1,5 @@
 name: ugrep
-version: 7.6.0
-mtime: 1772738388
-commit: c701fb852c8fe5ea48143bf809596470d5e2b248
+version: 7.7.0
+mtime: 1776792566
+commit: 5bfa7623a98e71c4ec1f2bd3046cb084a09ff82b
 

Reply via email to