Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ugrep for openSUSE:Factory checked 
in at 2023-04-03 00:52:01
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ugrep (Old)
 and      /work/SRC/openSUSE:Factory/.ugrep.new.9019 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ugrep"

Mon Apr  3 00:52:01 2023 rev:40 rq:1076764 version:3.11.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/ugrep/ugrep.changes      2023-03-19 
16:17:05.127470017 +0100
+++ /work/SRC/openSUSE:Factory/.ugrep.new.9019/ugrep.changes    2023-04-03 
00:52:01.766378443 +0200
@@ -1,0 +2,7 @@
+Sun Apr  2 18:28:20 UTC 2023 - Andreas Stieger <andreas.stie...@gmx.de>
+
+- update to 3.11.1:
+  * Smarter interactive navigation in the TUI when option --tree is
+    used with option -l or -c
+
+-------------------------------------------------------------------

Old:
----
  ugrep-3.11.0.tar.gz

New:
----
  ugrep-3.11.1.tar.gz

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

Other differences:
------------------
++++++ ugrep.spec ++++++
--- /var/tmp/diff_new_pack.qp4Y7z/_old  2023-04-03 00:52:02.270381008 +0200
+++ /var/tmp/diff_new_pack.qp4Y7z/_new  2023-04-03 00:52:02.274381029 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           ugrep
-Version:        3.11.0
+Version:        3.11.1
 Release:        0
 Summary:        Universal grep: a feature-rich grep implementation with focus 
on speed
 License:        BSD-3-Clause

++++++ ugrep-3.11.0.tar.gz -> ugrep-3.11.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/.github/workflows/c-cpp.yml 
new/ugrep-3.11.1/.github/workflows/c-cpp.yml
--- old/ugrep-3.11.0/.github/workflows/c-cpp.yml        1970-01-01 
01:00:00.000000000 +0100
+++ new/ugrep-3.11.1/.github/workflows/c-cpp.yml        2023-04-02 
15:59:38.000000000 +0200
@@ -0,0 +1,21 @@
+name: C/C++ CI
+
+on:
+  push:
+    branches: [ "master" ]
+  pull_request:
+    branches: [ "master" ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v3
+    - name: configure
+      run: ./configure
+    - name: make
+      run: make
+    - name: make test
+      run: make test
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/README.md new/ugrep-3.11.1/README.md
--- old/ugrep-3.11.0/README.md  2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/README.md  2023-04-02 15:59:38.000000000 +0200
@@ -1,4 +1,4 @@
-[![build status][travis-image]][travis-url] 
[![license][bsd-3-image]][bsd-3-url]
+[![build status][ci-image]][ci-url] [![license][bsd-3-image]][bsd-3-url]
 
 **ugrep v3.11 is now available: more features & even faster than before**
 
@@ -1886,17 +1886,17 @@
             Output NUM lines of trailing context after matching lines.  Places
             a --group-separator between contiguous groups of matches.  If -o is
             specified, output the match with context to fit NUM columns after
-            the match or shortening the match.  See also options -B, -C and -y.
+            the match or shortens the match.  See also options -B, -C and -y.
     -B NUM, --before-context=NUM
             Output NUM lines of leading context before matching lines.  Places
             a --group-separator between contiguous groups of matches.  If -o is
             specified, output the match with context to fit NUM columns before
-            the match or shortening the match.  See also options -A, -C and -y.
+            the match or shortens the match.  See also options -A, -C and -y.
     -C NUM, --context=NUM
             Output NUM lines of leading and trailing context surrounding each
             matching line.  Places a --group-separator between contiguous
             groups of matches.  If -o is specified, output the match with
-            context to fit NUM columns before and after the match or shortening
+            context to fit NUM columns before and after the match or shortens
             the match.  See also options -A, -B and -y.
     -y, --any-line
             Any line is output (passthru).  Non-matching lines are output as
@@ -3916,7 +3916,7 @@
                   Output NUM lines of trailing context after matching lines.  
Places
                   a --group-separator between contiguous groups of matches.  
If -o
                   is specified, output the match with context to fit NUM 
columns
-                  after the match or shortening the match.  See also options 
-B, -C
+                  after the match or shortens the match.  See also options -B, 
-C
                   and -y.
 
            -a, --text
@@ -3942,7 +3942,7 @@
                   Output NUM lines of leading context before matching lines.  
Places
                   a --group-separator between contiguous groups of matches.  
If -o
                   is specified, output the match with context to fit NUM 
columns
-                  before the match or shortening the match.  See also options 
-A, -C
+                  before the match or shortens the match.  See also options 
-A, -C
                   and -y.
 
            -b, --byte-offset
@@ -3998,8 +3998,8 @@
                   Output NUM lines of leading and trailing context surrounding 
each
                   matching line.  Places a --group-separator between contiguous
                   groups of matches.  If -o is specified, output the match with
-                  context to fit NUM columns before and after the match or
-                  shortening the match.  See also options -A, -B and -y.
+                  context to fit NUM columns before and after the match or 
shortens
+                  the match.  See also options -A, -B and -y.
 
            -c, --count
                   Only a count of selected lines is written to standard 
output.  If
@@ -4515,7 +4515,8 @@
            --separator[=SEP]
                   Use SEP as field separator between file name, line number, 
column
                   number, byte offset and the matched line.  The default is a 
colon
-                  (`:').
+                  (`:'), a plus (`+') for additional matches on the same line, 
and a
+                  bar (`|') for multi-line pattern matches.
 
            --sort[=KEY]
                   Displays matching files in the order specified by KEY in 
recursive
@@ -5258,7 +5259,7 @@
 
 
 
-    ugrep 3.11.0                     March 18, 2023                         
UGREP(1)
+    ugrep 3.11.1                      April 2, 2023                         
UGREP(1)
 
 🔝 [Back to table of contents](#toc)
 
@@ -5530,7 +5531,7 @@
 [report an issue](https://github.com/Genivia/ugrep/issues) on GitHub.  Bug
 reports are quickly addressed.
 
-[travis-image]: https://travis-ci.com/Genivia/ugrep.svg?branch=master
-[travis-url]: https://app.travis-ci.com/github/Genivia/ugrep
+[ci-image]: 
https://github.com/Genivia/ugrep/actions/workflows/c-cpp.yml/badge.svg
+[ci-url]: https://github.com/Genivia/ugrep/actions/workflows/c-cpp.yml
 [bsd-3-image]: https://img.shields.io/badge/license-BSD%203--Clause-blue.svg
 [bsd-3-url]: https://opensource.org/licenses/BSD-3-Clause
Binary files old/ugrep-3.11.0/bin/win32/ugrep.exe and 
new/ugrep-3.11.1/bin/win32/ugrep.exe differ
Binary files old/ugrep-3.11.0/bin/win64/ugrep.exe and 
new/ugrep-3.11.1/bin/win64/ugrep.exe differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/man/ugrep.1 new/ugrep-3.11.1/man/ugrep.1
--- old/ugrep-3.11.0/man/ugrep.1        2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/man/ugrep.1        2023-04-02 15:59:38.000000000 +0200
@@ -1,4 +1,4 @@
-.TH UGREP "1" "March 18, 2023" "ugrep 3.11.0" "User Commands"
+.TH UGREP "1" "April 02, 2023" "ugrep 3.11.1" "User Commands"
 .SH NAME
 \fBugrep\fR, \fBug\fR -- file pattern searcher
 .SH SYNOPSIS
@@ -58,7 +58,7 @@
 Output NUM lines of trailing context after matching lines.  Places
 a \fB\-\-group\-separator\fR between contiguous groups of matches.  If 
\fB\-o\fR is
 specified, output the match with context to fit NUM columns after
-the match or shortening the match.  See also options \fB\-B\fR, \fB\-C\fR and 
\fB\-y\fR.
+the match or shortens the match.  See also options \fB\-B\fR, \fB\-C\fR and 
\fB\-y\fR.
 .TP
 \fB\-a\fR, \fB\-\-text\fR
 Process a binary file as if it were text.  This is equivalent to
@@ -83,7 +83,7 @@
 Output NUM lines of leading context before matching lines.  Places
 a \fB\-\-group\-separator\fR between contiguous groups of matches.  If 
\fB\-o\fR is
 specified, output the match with context to fit NUM columns before
-the match or shortening the match.  See also options \fB\-A\fR, \fB\-C\fR and 
\fB\-y\fR.
+the match or shortens the match.  See also options \fB\-A\fR, \fB\-C\fR and 
\fB\-y\fR.
 .TP
 \fB\-b\fR, \fB\-\-byte\-offset\fR
 The offset in bytes of a matched line is displayed in front of the
@@ -136,7 +136,7 @@
 Output NUM lines of leading and trailing context surrounding each
 matching line.  Places a \fB\-\-group\-separator\fR between contiguous
 groups of matches.  If \fB\-o\fR is specified, output the match with
-context to fit NUM columns before and after the match or shortening
+context to fit NUM columns before and after the match or shortens
 the match.  See also options \fB\-A\fR, \fB\-B\fR and \fB\-y\fR.
 .TP
 \fB\-c\fR, \fB\-\-count\fR
@@ -643,7 +643,8 @@
 \fB\-\-separator\fR[=\fISEP\fR]
 Use SEP as field separator between file name, line number, column
 number, byte offset and the matched line.  The default is a colon
-(`:').
+(`:'), a plus (`+') for additional matches on the same line, and a
+bar (`|') for multi\-line pattern matches.
 .TP
 \fB\-\-sort\fR[=\fIKEY\fR]
 Displays matching files in the order specified by KEY in recursive
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/src/flag.hpp 
new/ugrep-3.11.1/src/flag.hpp
--- old/ugrep-3.11.0/src/flag.hpp       2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/src/flag.hpp       2023-04-02 15:59:38.000000000 +0200
@@ -175,6 +175,9 @@
 extern const char *flag_replace;
 extern const char *flag_save_config;
 extern const char *flag_separator;
+extern const char *flag_separator_dash;
+extern const char *flag_separator_plus;
+extern const char *flag_separator_bar;
 extern const char *flag_sort;
 extern const char *flag_stats;
 extern const char *flag_tag;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/src/output.cpp 
new/ugrep-3.11.1/src/output.cpp
--- old/ugrep-3.11.0/src/output.cpp     2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/src/output.cpp     2023-04-02 15:59:38.000000000 +0200
@@ -30,7 +30,7 @@
 @file      output.cpp
 @brief     Output management
 @author    Robert van Engelen - enge...@genivia.com
-@copyright (c) 2019-2022, Robert van Engelen, Genivia Inc. All rights reserved.
+@copyright (c) 2019-2023, Robert van Engelen, Genivia Inc. All rights reserved.
 @copyright (c) BSD-3 License - see LICENSE.txt
 */
 
@@ -224,7 +224,7 @@
     dump.done();
 
   bool sep = false; // when a separator is needed
-  bool nul = false; // -Q: mark pathname with three NUL bytes unless -a
+  bool nul = false; // -Q: mark pathname with three \0 markers unless -a
 
   if (flag_with_filename && pathname != NULL)
   {
@@ -360,10 +360,10 @@
   }
 }
 
-// output the pathname header for --files_with_matches and --count
+// output the short pathname header for --files_with_matches and --count
 void Output::header(const char *pathname, const std::string& partname)
 {
-  bool nul = flag_query > 0; // -Q: mark pathname with three NUL bytes
+  bool nul = flag_query > 0; // -Q: mark pathname with three \0 markers for 
quick navigation
 
   if (flag_tree)
   {
@@ -377,6 +377,7 @@
       Tree::path.pop_back();
 
       size_t len = Tree::path.rfind(PATHSEPCHR);
+
       if (len == std::string::npos)
         Tree::path.clear();
       else
@@ -390,25 +391,39 @@
     {
       for (int i = 0; i < Tree::depth; ++i)
         str(Tree::bar);
-      while (--up > 0)
+      for (int i = 1; i < up; ++i)
         str(Tree::end);
       nl();
+
+      // make sure to add a break between trees with terminated leafs
+      if (up > 1 && *Tree::end != '\0' && Tree::depth == 0)
+        nl();
+    }
+    else if (Tree::path.empty() && strchr(pathname, PATHSEPCHR) != NULL)
+    {
+      // add a break between the list of filenames without path and filenames 
with paths
+      nl();
     }
 
     const char *sep;
 
     while ((sep = strchr(pathname + Tree::path.size(), PATHSEPCHR)) != NULL)
     {
+      if (nul)
+        chr('\0');
+
       for (int i = 1; i < Tree::depth; ++i)
         str(Tree::bar);
+
       if (Tree::depth > 0)
         str(Tree::ptr);
-      else if (nul)
-        str("\0\0", 2);
+
+      if (nul)
+        chr('\0');
 
       str(pathname + Tree::path.size(), sep - (pathname + Tree::path.size()) + 
1);
 
-      if (nul && Tree::depth == 0)
+      if (nul)
         chr('\0');
       nl();
 
@@ -416,12 +431,14 @@
       ++Tree::depth;
     }
 
+    if (nul)
+      chr('\0');
+
     for (int i = 1; i < Tree::depth; ++i)
       str(Tree::bar);
+
     if (Tree::depth > 0)
       str(Tree::ptr);
-    else if (nul)
-      chr('\0');
 
     str(color_fn);
 
@@ -433,12 +450,12 @@
       str(color_st);
     }
 
-    if (nul && Tree::depth == 0)
+    if (nul)
       chr('\0');
 
     str(pathname + Tree::path.size());
 
-    if (nul && Tree::depth == 0)
+    if (nul)
       chr('\0');
 
   }
@@ -488,9 +505,11 @@
 {
   if ((mode_ & BINARY) != 0)
     return;
+
   str(color_off);
   str("Binary file");
   str(color_fn);
+
   if (pathname != NULL)
   {
     chr(' ');
@@ -508,6 +527,7 @@
       str(color_st);
     }
   }
+
   if (!partname.empty())
   {
     if (pathname == NULL)
@@ -516,9 +536,11 @@
     str(partname);
     chr('}');
   }
+
   str(color_off);
   str(" matches");
   nl();
+
   mode_ |= BINARY;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/src/output.hpp 
new/ugrep-3.11.1/src/output.hpp
--- old/ugrep-3.11.0/src/output.hpp     2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/src/output.hpp     2023-04-02 15:59:38.000000000 +0200
@@ -30,7 +30,7 @@
 @file      output.hpp
 @brief     Output management
 @author    Robert van Engelen - enge...@genivia.com
-@copyright (c) 2019-2022, Robert van Engelen, Genivia Inc. All rights reserved.
+@copyright (c) 2019-2023, Robert van Engelen, Genivia Inc. All rights reserved.
 @copyright (c) BSD-3 License - see LICENSE.txt
 */
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/src/query.cpp 
new/ugrep-3.11.1/src/query.cpp
--- old/ugrep-3.11.0/src/query.cpp      2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/src/query.cpp      2023-04-02 15:59:38.000000000 +0200
@@ -30,7 +30,7 @@
 @file      query.cpp
 @brief     Query engine and UI
 @author    Robert van Engelen - enge...@genivia.com
-@copyright (c) 2019-2022, Robert van Engelen, Genivia Inc. All rights reserved.
+@copyright (c) 2019-2023, Robert van Engelen, Genivia Inc. All rights reserved.
 @copyright (c) BSD-3 License - see LICENSE.txt
 */
 
@@ -1357,7 +1357,7 @@
   row_ = 0;
   rows_ = 0;
   skip_ = 0;
-  dots_ = 6; // to clear screen after 300ms
+  dots_ = 6; // to clear screen after 300 ms
   error_ = -1;
   arg_pattern = globbing_ ? temp_ : line_;
 
@@ -1434,8 +1434,8 @@
         Screen::put(rows_ - row_ + 1, 0, eof_ ? "(END)" : searching_);
         Screen::normal();
 
-        // when still searching, don't immediately clear the rest of the 
screen but clear after 300ms (init dots_ = 6 and three iters)
-        if (dots_ == 0)
+        // when still searching, don't immediately clear the rest of the 
screen to avoid screen flicker, clear after 300 ms (init dots_ = 6 and three 
iters)
+        if (eof_ || dots_ == 0)
           Screen::end();
       }
 
@@ -1647,10 +1647,10 @@
   }
 }
 
-// execute the search in a new thread
-void Query::execute(int fd)
+// execute the search in a new thread and send results to a pipe
+void Query::execute(int pipe_fd)
 {
-  output = fdopen(fd, "wb");
+  output = fdopen(pipe_fd, "wb");
 
   if (output != NULL)
   {
@@ -1871,40 +1871,44 @@
   // compare the directory part for options -l and -c to move between 
directories
   bool compare_dir = flag_files_with_matches || flag_count;
 
-  std::string filename;
-  bool found = false;
+  // scroll current row_ or select_ selection
+  int& ref = select_ == -1 ? row_ : select_;
 
-  if (select_ == -1)
-  {
-    if (row_ == 0)
-      return;
+  if (ref == 0)
+    return;
 
-    --row_;
+  --ref;
 
-    // get the current filename to compare when present
-    is_filename(view_[row_], filename);
+  if (compare_dir && flag_tree)
+  {
+    if (ref == 0)
+      return;
 
-    while (row_ > 0 && !(found = is_filename(view_[row_], filename, 
compare_dir)))
-      --row_;
+    --ref;
 
-    if (found && !flag_heading)
-      ++row_;
+    while (ref > 0 && view_[ref].size() > 1)
+      --ref;
   }
   else
   {
-    if (select_ == 0)
-      return;
+    std::string filename;
 
-    --select_;
+    // get the current filename to compare against, when present
+    find_filename(ref, filename);
 
-    // get the current filename to compare when present
-    is_filename(view_[select_], filename);
+    bool found = false;
+
+    while (ref > 0 && !(found = find_filename(ref, filename, compare_dir)))
+      --ref;
 
-    while (select_ > 0 && !(found = is_filename(view_[select_], filename, 
compare_dir)))
-      --select_;
+    if (found && (compare_dir || !flag_heading))
+    {
+      ++ref;
 
-    if (found && !flag_heading)
-      ++select_;
+      // --tree: skip over directory tree spacing
+      if (compare_dir && flag_tree && (view_[ref].empty() || 
view_[ref].front() != '\0'))
+        ++ref;
+    }
   }
 
   redraw();
@@ -1924,30 +1928,25 @@
   // compare the directory part for options -l and -c to move between 
directories
   bool compare_dir = flag_files_with_matches || flag_count;
 
-  std::string filename;
+  // scroll current row_ or select_ selection
+  int& ref = select_ == -1 ? row_ : select_;
 
-  if (select_ == -1)
+  if (compare_dir && flag_tree)
   {
-    if (row_ < rows_)
-    {
-      // get the current filename to compare when present
-      is_filename(view_[row_], filename);
-    }
-
-    ++row_;
+    ++ref;
 
     while (true)
     {
       bool found = false;
 
-      while (row_ + 1 < rows_ && !(found = is_filename(view_[row_], filename, 
compare_dir)))
-        ++row_;
-
-      if (found || (eof_ && buflen_ == 0))
-        break;
+      while (ref + 1 < rows_ && !(found = view_[ref].size() <= 1))
+        ++ref;
 
       redraw();
 
+      if (found || (eof_ && buflen_ == 0))
+        return;
+
       // fetch more search results when available
       if (update())
       {
@@ -1965,26 +1964,27 @@
   }
   else
   {
-    if (select_ < rows_)
-    {
-      // get the current filename to compare when present
-      is_filename(view_[select_], filename);
-    }
+    std::string filename;
 
-    ++select_;
+    // get the current filename to compare when present
+    if (ref < rows_)
+      find_filename(ref, filename);
+
+    ++ref;
 
     while (true)
     {
       bool found = false;
 
-      while (select_ + 1 < rows_ && !(found = is_filename(view_[select_], 
filename, compare_dir)))
-        ++select_;
-
-      if (found || (eof_ && buflen_ == 0))
-        break;
+      // seach forward for different filename or different directory
+      while (ref + 1 < rows_ && !(found = find_filename(ref, filename, 
compare_dir)))
+        ++ref;
 
       redraw();
 
+      if (found || (eof_ && buflen_ == 0))
+        return;
+
       // fetch more search results when available
       if (update())
       {
@@ -2000,8 +2000,6 @@
       }
     }
   }
-
-  redraw();
 }
 
 // jump to the specified row
@@ -2010,87 +2008,45 @@
   if (row < 0)
     row = 0;
 
-  if (select_ == -1)
-  {
-    if (row <= row_)
-    {
-      row_ = row;
-
-      if (row_ >= rows_)
-        row_ = rows_ - 1;
-    }
-    else if (row < rows_)
-    {
-      row_ = row;
-    }
-    else
-    {
-      while (true)
-      {
-        while (row_ < row && row_ + 1 < rows_)
-          ++row_;
-
-        // exit if at the desired row or if the desired row is beyond the end 
of the search results
-        if (row_ == row || (eof_ && buflen_ == 0))
-          break;
+  // scroll current row_ or select_ selection
+  int& ref = select_ == -1 ? row_ : select_;
 
-        redraw();
+  if (row <= ref)
+  {
+    ref = row;
 
-        // fetch more search results when available
-        if (update())
-        {
-          // poll keys without timeout and stop if a key was pressed
-          if (VKey::poll(0))
-            return;
-        }
-        else
-        {
-          // poll keys with 100 ms timeout and stop if a key was pressed
-          if (VKey::poll(100))
-            return;
-        }
-      }
-    }
+    if (ref >= rows_)
+      ref = rows_ - 1;
+  }
+  else if (row < rows_)
+  {
+    ref = row;
   }
   else
   {
-    if (row <= select_)
-    {
-      select_ = row;
-
-      if (select_ >= rows_)
-        select_ = rows_ - 1;
-    }
-    else if (row < rows_)
-    {
-      select_ = row;
-    }
-    else
+    while (true)
     {
-      while (true)
-      {
-        while (select_ < row && select_ + 1 < rows_)
-          ++select_;
+      while (ref < row && ref + 1 < rows_)
+        ++ref;
 
-        // exit if at the desired row or if the desired row is beyond the end 
of the search results
-        if (select_ == row || (eof_ && buflen_ == 0))
-          break;
+      // exit if at the desired row or if the desired row is beyond the end of 
the search results
+      if (ref == row || (eof_ && buflen_ == 0))
+        break;
 
-        redraw();
+      redraw();
 
-        // fetch more search results when available
-        if (update())
-        {
-          // poll keys without timeout and stop if a key was pressed
-          if (VKey::poll(0))
-            return;
-        }
-        else
-        {
-          // poll keys with 100 ms timeout and stop if a key was pressed
-          if (VKey::poll(100))
-            return;
-        }
+      // fetch more search results when available
+      if (update())
+      {
+        // poll keys without timeout and stop if a key was pressed
+        if (VKey::poll(0))
+          return;
+      }
+      else
+      {
+        // poll keys with 100 ms timeout and stop if a key was pressed
+        if (VKey::poll(100))
+          return;
       }
     }
   }
@@ -2111,11 +2067,13 @@
   if (flag_stdin)
   {
     message("cannot view or edit standard input");
+
     return;
   }
 
   const char *pager = flag_view;
 
+  // if --view is empty and not --no-view, then try the PAGER or EDITOR 
environment variable as viewer or default viewer
   if (pager != NULL && *pager == '\0')
   {
     pager = getenv("PAGER");
@@ -2127,6 +2085,7 @@
       pager = DEFAULT_VIEW_COMMAND;
   }
 
+  // if no viewer, then give up
   if (pager == NULL || *pager == '\0')
   {
     Screen::alert();
@@ -2134,12 +2093,21 @@
     return;
   }
 
+  int row = select_ >= 0 ? select_ : row_;
+
+  // --tree: move down over non-filename lines to reach a filename
+  if (flag_tree && (flag_files_with_matches || flag_count))
+  {
+    while (row + 1 < rows_ && (view_[row].empty() || view_[row].front() != 
'\0'))
+      ++row;
+  }
+
   std::string filename;
   bool found = false;
-  int row;
 
-  for (row = row_; row >= 0 && !(found = is_filename(view_[row], filename)); 
--row)
-    continue;
+  // move up until a filename header is found
+  while (row >= 0 && !(found = find_filename(row, filename)))
+    --row;
 
   if (!found && arg_files.size() == 1)
   {
@@ -2174,6 +2142,17 @@
 
       if (system(command.c_str()) == 0)
       {
+#ifdef OS_WIN
+        if (pager == "more")
+        {
+          Screen::setpos(Screen::rows - 1, 0);
+          Screen::put("(END) press a key");
+          Screen::alert();
+          VKey::flush();
+          VKey::get();
+        }
+#endif
+
         Screen::clear();
 
         bool changed;
@@ -2207,14 +2186,15 @@
       }
       else
       {
-        Screen::put(0, 0, command.c_str());
         Screen::alert();
+        redraw();
+        message(std::string("failed: ").append(command));
       }
     }
   }
 
   if (!found)
-    message(std::string("cannot edit file ").append(filename));
+    message(std::string("cannot view or edit ").append(filename));
 }
 
 // chdir one level down into the directory of the file located under the 
cursor or just above the screen
@@ -2234,11 +2214,21 @@
     return;
   }
 
+  int row = select_ >= 0 ? select_ : row_;
+
+  // --tree: move down over non-filename lines to reach a filename
+  if (flag_tree && (flag_files_with_matches || flag_count))
+  {
+    while (row + 1 < rows_ && (view_[row].empty() || view_[row].front() != 
'\0'))
+      ++row;
+  }
+
   std::string pathname;
   bool found = false;
 
-  for (int i = select_ >= 0 ? select_ : row_; i >= 0 && !(found = 
is_filename(view_[i], pathname)); --i)
-    continue;
+  // move up until a filename header is found
+  while (row >= 0 && !(found = find_filename(row, pathname)))
+    --row;
 
   if (found)
   {
@@ -3117,7 +3107,7 @@
   const char *text = line.c_str();
   const char *end = text + line.size();
 
-  // how many nulls to ignore, part of filename marking?
+  // how many NULs to ignore that are part of the pathname marking in headers?
   int nulls = *text == '\0' && !flag_text ? 2 : 0;
 
   if (nulls > 0)
@@ -3510,9 +3500,10 @@
   return nwritten;
 }
 
-// true if line starts with a valid filename/filepath identified by three \0 
markers and differs from the given filename, then assigns filename
-bool Query::is_filename(const std::string& line, std::string& filename, bool 
compare_dir)
+// true if view_[ref] starts with a valid filename/filepath identified by 
three \0 markers and differs from the given filename, then assigns filename
+bool Query::find_filename(int ref, std::string& filename, bool compare_dir)
 {
+  const std::string& line = view_[ref];
   size_t end = line.size();
 
   if (end < 4 || line.front() != '\0')
@@ -3534,26 +3525,72 @@
   if (pos == start || pos >= end)
     return false;
 
+  // extract the new filename
+  std::string new_filename = line.substr(start, pos - start);
+
+  // --tree: the new filename is the basename, we should reconstruct the new 
pathname from the tree in view_[]
+  if (flag_tree && (flag_files_with_matches || flag_count))
+  {
+    size_t last_start = start;
+
+    // while the top directory was not found (works since no colors are used 
for directory names)
+    while (start > 2 && --ref >= 0)
+    {
+      const std::string& scanline = view_[ref];
+
+      end = scanline.size();
+
+      if (end <= 1)
+        break;
+
+      if (end < 4 || scanline.front() != '\0')
+        continue;
+
+      pos = 1;
+
+      while (pos < end && scanline.at(pos) != '\0')
+        ++pos;
+
+      if (++pos >= end)
+        continue;
+
+      start = pos;
+
+      while (pos < end && scanline.at(pos) != '\0')
+        ++pos;
+
+      if (pos == start || pos >= end)
+        continue;
+
+      // if line scanned represents a parent directory, then prepend the 
directory to the new filename
+      if (start < last_start && scanline.at(pos - 1) == PATHSEPCHR)
+      {
+        new_filename.insert(0, scanline, start, pos - start);
+        last_start = start;
+      }
+    }
+  }
+
   if (compare_dir)
   {
+    // the new filename must not be in the same directory as the old filename
     size_t skip = 0;
 #ifdef OS_WIN
     if (filename.size() >= 3 && filename.at(1) == ':' && filename.at(2) == 
PATHSEPCHR)
       skip = 3;
 #endif
-    size_t pos1 = line.find(PATHSEPCHR, start + skip);
+    size_t pos1 = new_filename.find(PATHSEPCHR, skip);
     size_t pos2 = filename.find(PATHSEPCHR, skip);
-    if (pos1 != std::string::npos)
-      pos1 -= start;
-    if (pos1 == pos2 && (pos1 == std::string::npos || line.compare(start, skip 
+ pos1, filename, 0, skip + pos2) == 0))
-      return false; // the extracted filename is the same or in the same 
directory as the previous
+
+    if (pos1 == pos2 && (pos1 == std::string::npos || new_filename.compare(0, 
pos1, filename, 0, pos2) == 0))
+      return false; // the extracted filename is the same or is in the same 
directory as the old filename
   }
-  else if (line.compare(start, pos - start, filename) == 0)
+  else if (new_filename.compare(filename) == 0)
   {
-    return false; // the extracted filename is the same as the previous
+    return false; // the new filename is the same as the old filename
   }
 
-  filename = line.substr(start, pos - start);
+  filename.swap(new_filename);
 
   return true;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/src/query.hpp 
new/ugrep-3.11.1/src/query.hpp
--- old/ugrep-3.11.0/src/query.hpp      2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/src/query.hpp      2023-04-02 15:59:38.000000000 +0200
@@ -30,7 +30,7 @@
 @file      query.hpp
 @brief     Query engine and UI
 @author    Robert van Engelen - enge...@genivia.com
-@copyright (c) 2019-2022, Robert van Engelen, Genivia Inc. All rights reserved.
+@copyright (c) 2019-2023, Robert van Engelen, Genivia Inc. All rights reserved.
 @copyright (c) BSD-3 License - see LICENSE.txt
 */
 
@@ -266,7 +266,7 @@
 
   static ssize_t stdin_sender(int fd);
 
-  static bool is_filename(const std::string& line, std::string& filename, bool 
compare_dir = false);
+  static bool find_filename(int ref, std::string& filename, bool compare_dir = 
false);
 
   static Mode                     mode_;
   static bool                     updated_;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/src/ugrep.cpp 
new/ugrep-3.11.1/src/ugrep.cpp
--- old/ugrep-3.11.0/src/ugrep.cpp      2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/src/ugrep.cpp      2023-04-02 15:59:38.000000000 +0200
@@ -30,7 +30,7 @@
 @file      ugrep.cpp
 @brief     a pattern search utility written in C++11
 @author    Robert van Engelen - enge...@genivia.com
-@copyright (c) 2019-2022, Robert van Engelen, Genivia Inc. All rights reserved.
+@copyright (c) 2019-2023, Robert van Engelen, Genivia Inc. All rights reserved.
 @copyright (c) BSD-3 License - see LICENSE.txt
 
 For download and installation instructions:
@@ -415,7 +415,10 @@
 const char *flag_pager             = DEFAULT_PAGER;
 const char *flag_replace           = NULL;
 const char *flag_save_config       = NULL;
-const char *flag_separator         = ":";
+const char *flag_separator         = NULL;
+const char *flag_separator_dash    = "-";
+const char *flag_separator_plus    = "+";
+const char *flag_separator_bar     = "|";
 const char *flag_sort              = NULL;
 const char *flag_stats             = NULL;
 const char *flag_tag               = NULL;
@@ -2160,7 +2163,7 @@
       // context colors with or without -v
       short v_hex_context_line = flag_invert_match ? Output::Dump::HEX_LINE : 
Output::Dump::HEX_CONTEXT_LINE;
       const char *v_color_cx = flag_invert_match ? color_sl : color_cx;
-      const char *separator = flag_invert_match ? flag_separator : "-";
+      const char *separator = flag_invert_match ? flag_separator : 
flag_separator_dash;
 
       while (ptr != NULL)
       {
@@ -2304,7 +2307,7 @@
             grep.out.dump.done();
 
           if (!flag_no_header)
-            grep.out.header(pathname, grep.partname, before_lineno + i, NULL, 
state.before_offset[j], "-", state.before_binary[j]);
+            grep.out.header(pathname, grep.partname, before_lineno + i, NULL, 
state.before_offset[j], flag_separator_dash, state.before_binary[j]);
 
           hex = state.before_binary[j];
 
@@ -2429,7 +2432,7 @@
             grep.out.dump.done();
 
           if (!flag_no_header)
-            grep.out.header(pathname, grep.partname, lineno, NULL, offset, 
"-", binary);
+            grep.out.header(pathname, grep.partname, lineno, NULL, offset, 
flag_separator_dash, binary);
 
           hex = binary;
 
@@ -2559,7 +2562,7 @@
             grep.out.dump.done();
 
           if (!flag_no_header)
-            grep.out.header(pathname, grep.partname, before_lineno + i, NULL, 
offset, "-", state.before_binary[j]);
+            grep.out.header(pathname, grep.partname, before_lineno + i, NULL, 
offset, flag_separator_dash, state.before_binary[j]);
 
           hex = state.before_binary[j];
 
@@ -4220,7 +4223,7 @@
   fprintf(file, "# Enable/disable color\n%s\n\n", flag_color != NULL ? "color" 
: "no-color");
   fprintf(file, "# Enable/disable query UI confirmation prompts, default: 
confirm\n%s\n\n", flag_confirm ? "confirm" : "no-confirm");
 
-  fprintf(file, "# Enable/disable query UI file viewing command with CTRL-Y, 
default: view\n");
+  fprintf(file, "# Enable/disable query UI file viewing command with CTRL-Y or 
F2, default: view\n");
   if (flag_view != NULL && *flag_view == '\0')
     fprintf(file, "view\n\n");
   else if (flag_view != NULL)
@@ -4298,6 +4301,12 @@
   fprintf(file, "# Enable/disable binary files, default: 
no-ignore-binary\n%s\n\n", strcmp(flag_binary_files, "without-match") == 0 ? 
"ignore-binary" : "no-ignore-binary");
   fprintf(file, "# Enable/disable decompression and archive search, default: 
no-decompress\n%s\n\n", flag_decompress ? "decompress" : "no-decompress");
   fprintf(file, "# Maximum decompression and de-archiving nesting levels, 
default: 1\nzmax=%zu\n\n", flag_zmax);
+  if (flag_dereference)
+    fprintf(file, "# Dereference symlinks, default: 
no-dereference\ndereference\n\n");
+  if (flag_devices != NULL)
+    fprintf(file, "# Search devices, default: devices=skip\ndevices=%s\n\n", 
flag_devices);
+  if (flag_max_depth > 0)
+    fprintf(file, "# Recursively search directories up to %zu levels 
deep\nmax-depth=%zu\n\n", flag_max_depth, flag_max_depth);
   if (flag_ignore_files.empty())
   {
     fprintf(file, "# Enable/disable ignore files, default: 
no-ignore-files\nno-ignore-files\n\n");
@@ -4323,6 +4332,9 @@
 
   fprintf(file, "### OUTPUT ###\n\n");
 
+  if (flag_separator != NULL)
+    fprintf(file, "# Separator, default: none specified to output a `:', a 
`+', and a `|'\nseparator=%s\n\n", flag_separator);
+
   fprintf(file, "# Enable/disable sorted output, default: no-sort\n");
   if (flag_sort != NULL)
     fprintf(file, "sort=%s\n\n", flag_sort);
@@ -4813,7 +4825,7 @@
                 else if (strncmp(arg, "save-config=", 12) == 0)
                   flag_save_config = arg + 12;
                 else if (strcmp(arg, "separator") == 0)
-                  flag_separator = ":";
+                  flag_separator = NULL;
                 else if (strncmp(arg, "separator=", 10) == 0)
                   flag_separator = arg + 10;
                 else if (strcmp(arg, "silent") == 0)
@@ -5620,6 +5632,12 @@
     exit(EXIT_ERROR);
   }
 
+  // --separator: override :, + and |, otherwise use default separators :, +, 
and |
+  if (flag_separator == NULL || *flag_separator == '\0')
+    flag_separator = ":";
+  else
+    flag_separator_bar = flag_separator_plus = flag_separator;
+
 #ifdef OS_WIN
   // save_config() and help() assume text mode, so switch to
   // binary after we're no longer going to call them.
@@ -9073,7 +9091,7 @@
 
           if (!flag_no_header)
           {
-            const char *separator = lineno != current_lineno ? flag_separator 
: "+";
+            const char *separator = lineno != current_lineno ? flag_separator 
: flag_separator_plus;
             out.header(pathname, partname, current_lineno, matcher, 
matcher->first(), separator, binary);
           }
 
@@ -9238,7 +9256,7 @@
               out.str(match_off);
               out.chr('\n');
 
-              out.header(pathname, partname, ++lineno, NULL, matcher->first() 
+ (to - begin) + 1, "|", false);
+              out.header(pathname, partname, ++lineno, NULL, matcher->first() 
+ (to - begin) + 1, flag_separator_bar, false);
 
               from = to + 1;
             }
@@ -9427,7 +9445,7 @@
 
             if (!flag_no_header)
             {
-              const char *separator = lineno != current_lineno ? 
flag_separator : "+";
+              const char *separator = lineno != current_lineno ? 
flag_separator : flag_separator_plus;
               out.header(pathname, partname, current_lineno, matcher, first, 
separator, binary);
             }
 
@@ -9499,7 +9517,7 @@
                   out.str(match_off);
                   out.chr('\n');
 
-                  out.header(pathname, partname, ++lineno, NULL, first + (to - 
begin) + 1, "|", false);
+                  out.header(pathname, partname, ++lineno, NULL, first + (to - 
begin) + 1, flag_separator_bar, false);
 
                   from = to + 1;
                 }
@@ -9580,7 +9598,7 @@
                       if (left < first - restline_last)
                       {
                         if (!flag_no_header)
-                          out.header(pathname, partname, current_lineno, 
matcher, first, "+", binary);
+                          out.header(pathname, partname, current_lineno, 
matcher, first, flag_separator_plus, binary);
 
                         left = first - restline_last - left;
 
@@ -9624,7 +9642,7 @@
                         out.str(match_off);
                         out.chr('\n');
 
-                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, "|", false);
+                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, flag_separator_bar, false);
 
                         from = to + 1;
                         ++num;
@@ -10010,7 +10028,7 @@
 
             if (!flag_no_header)
             {
-              const char *separator = lineno != current_lineno ? 
flag_invert_match ? "-" : flag_separator : "+";
+              const char *separator = lineno != current_lineno ? 
flag_invert_match ? flag_separator_dash : flag_separator : flag_separator_plus;
               out.header(pathname, partname, current_lineno, matcher, first, 
separator, binary);
             }
 
@@ -10065,7 +10083,7 @@
                   out.str(match_off);
                   out.chr('\n');
 
-                  out.header(pathname, partname, ++lineno, NULL, first + (to - 
begin) + 1, "|", false);
+                  out.header(pathname, partname, ++lineno, NULL, first + (to - 
begin) + 1, flag_separator_bar, false);
 
                   from = to + 1;
                 }
@@ -10163,7 +10181,7 @@
                         out.str(match_off);
                         out.chr('\n');
 
-                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, "|", false);
+                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, flag_separator_bar, false);
 
                         from = to + 1;
                         ++num;
@@ -10456,7 +10474,7 @@
 
             if (!flag_no_header)
             {
-              const char *separator = lineno != current_lineno ? 
flag_invert_match ? "-" : flag_separator : "+";
+              const char *separator = lineno != current_lineno ? 
flag_invert_match ? flag_separator_dash : flag_separator : flag_separator_plus;
               out.header(pathname, partname, current_lineno, matcher, first, 
separator, binary);
             }
 
@@ -10521,7 +10539,7 @@
                   out.str(match_off);
                   out.chr('\n');
 
-                  out.header(pathname, partname, ++lineno, NULL, first + (to - 
begin) + 1, "|", false);
+                  out.header(pathname, partname, ++lineno, NULL, first + (to - 
begin) + 1, flag_separator_bar, false);
 
                   from = to + 1;
                 }
@@ -10619,7 +10637,7 @@
                         out.str(match_off);
                         out.chr('\n');
 
-                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, "|", false);
+                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, flag_separator_bar, false);
 
                         from = to + 1;
                         ++num;
@@ -10918,7 +10936,7 @@
                 out.dump.done();
 
               if (!flag_no_header)
-                out.header(pathname, partname, lineno, matcher, first, "-", 
binary);
+                out.header(pathname, partname, lineno, matcher, first, 
flag_separator_dash, binary);
 
               hex = binary;
 
@@ -10965,7 +10983,7 @@
                       out.str(match_off);
                       out.chr('\n');
 
-                      out.header(pathname, partname, lineno + num, NULL, first 
+ (to - begin) + 1, "-", false);
+                      out.header(pathname, partname, lineno + num, NULL, first 
+ (to - begin) + 1, flag_separator_dash, false);
 
                       from = to + 1;
                       ++num;
@@ -11099,7 +11117,7 @@
                         out.str(match_off);
                         out.chr('\n');
 
-                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, "-", false);
+                        out.header(pathname, partname, lineno + num, NULL, 
first + (to - begin) + 1, flag_separator_dash, false);
 
                         from = to + 1;
                         ++num;
@@ -11134,7 +11152,7 @@
                     out.nl();
 
                   if (hex != binary && !flag_no_header)
-                    out.header(pathname, partname, lineno + lines - 1, 
matcher, last, "-", binary);
+                    out.header(pathname, partname, lineno + lines - 1, 
matcher, last, flag_separator_dash, binary);
 
                   hex = binary;
 
@@ -11695,7 +11713,7 @@
             Output NUM lines of trailing context after matching lines.  
Places\n\
             a --group-separator between contiguous groups of matches.  If -o 
is\n\
             specified, output the match with context to fit NUM columns 
after\n\
-            the match or shortening the match.  See also options -B, -C and 
-y.\n\
+            the match or shortens the match.  See also options -B, -C and 
-y.\n\
     -a, --text\n\
             Process a binary file as if it were text.  This is equivalent to\n\
             the --binary-files=text option.  This option might output binary\n\
@@ -11716,7 +11734,7 @@
             Output NUM lines of leading context before matching lines.  
Places\n\
             a --group-separator between contiguous groups of matches.  If -o 
is\n\
             specified, output the match with context to fit NUM columns 
before\n\
-            the match or shortening the match.  See also options -A, -C and 
-y.\n\
+            the match or shortens the match.  See also options -A, -C and 
-y.\n\
     -b, --byte-offset\n\
             The offset in bytes of a matched line is displayed in front of 
the\n\
             respective matched line.  If -u is specified, displays the 
offset\n\
@@ -11764,7 +11782,7 @@
             Output NUM lines of leading and trailing context surrounding 
each\n\
             matching line.  Places a --group-separator between contiguous\n\
             groups of matches.  If -o is specified, output the match with\n\
-            context to fit NUM columns before and after the match or 
shortening\n\
+            context to fit NUM columns before and after the match or 
shortens\n\
             the match.  See also options -A, -B and -y.\n\
     -c, --count\n\
             Only a count of selected lines is written to standard output.\n\
@@ -12231,7 +12249,8 @@
     --separator[=SEP]\n\
             Use SEP as field separator between file name, line number, 
column\n\
             number, byte offset and the matched line.  The default is a 
colon\n\
-            (`:').\n\
+            (`:'), a plus (`+') for additional matches on the same line, and 
a\n\
+            bar (`|') for multi-line pattern matches.\n\
     --sort[=KEY]\n\
             Displays matching files in the order specified by KEY in 
recursive\n\
             searches.  Normally the ug command sorts by name whereas the 
ugrep\n\
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ugrep-3.11.0/src/ugrep.hpp 
new/ugrep-3.11.1/src/ugrep.hpp
--- old/ugrep-3.11.0/src/ugrep.hpp      2023-03-19 02:07:00.000000000 +0100
+++ new/ugrep-3.11.1/src/ugrep.hpp      2023-04-02 15:59:38.000000000 +0200
@@ -30,7 +30,7 @@
 @file      ugrep.hpp
 @brief     a pattern search utility written in C++11
 @author    Robert van Engelen - enge...@genivia.com
-@copyright (c) 2019-2022, Robert van Engelen, Genivia Inc. All rights reserved.
+@copyright (c) 2019-2023, Robert van Engelen, Genivia Inc. All rights reserved.
 @copyright (c) BSD-3 License - see LICENSE.txt
 */
 
@@ -38,7 +38,7 @@
 #define UGREP_HPP
 
 // ugrep version
-#define UGREP_VERSION "3.11.0"
+#define UGREP_VERSION "3.11.1"
 
 // disable mmap because mmap is almost always slower than the file reading 
speed improvements since 3.0.0
 #define WITH_NO_MMAP
@@ -348,7 +348,7 @@
 // the default -Q UI view command when --view is used and PAGER or EDITOR are 
not set
 #ifndef DEFAULT_VIEW_COMMAND
 # ifdef OS_WIN
-#  define DEFAULT_VIEW_COMMAND "ugrep"
+#  define DEFAULT_VIEW_COMMAND "more"
 # else
 #  define DEFAULT_VIEW_COMMAND "less"
 # endif

Reply via email to