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-03-02 23:03:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ugrep (Old) and /work/SRC/openSUSE:Factory/.ugrep.new.31432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ugrep" Thu Mar 2 23:03:39 2023 rev:37 rq:1068539 version:3.10.0 Changes: -------- --- /work/SRC/openSUSE:Factory/ugrep/ugrep.changes 2023-02-01 16:39:52.077916133 +0100 +++ /work/SRC/openSUSE:Factory/.ugrep.new.31432/ugrep.changes 2023-03-02 23:04:11.692061157 +0100 @@ -1,0 +2,10 @@ +Wed Mar 1 19:42:06 UTC 2023 - Andreas Stieger <andreas.stie...@gmx.de> + +- update to 3.10.0: + * This release adds the option --tree to output directory trees + of files for the options -l (--files-with-matches), + -L (--files-withou-match), and -c (--count) + * The option --pretty was updated to to enable --tree when output + is sent to a terminal. This can be disabled with --no-tree. + +------------------------------------------------------------------- Old: ---- ugrep-3.9.7.tar.gz New: ---- ugrep-3.10.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ugrep.spec ++++++ --- /var/tmp/diff_new_pack.FM67gY/_old 2023-03-02 23:04:12.484065114 +0100 +++ /var/tmp/diff_new_pack.FM67gY/_new 2023-03-02 23:04:12.488065134 +0100 @@ -17,7 +17,7 @@ Name: ugrep -Version: 3.9.7 +Version: 3.10.0 Release: 0 Summary: Universal grep: a feature-rich grep implementation with focus on speed License: BSD-3-Clause ++++++ ugrep-3.9.7.tar.gz -> ugrep-3.10.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/Makefile.am new/ugrep-3.10.0/Makefile.am --- old/ugrep-3.9.7/Makefile.am 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/Makefile.am 2023-03-01 02:59:57.000000000 +0100 @@ -18,14 +18,14 @@ cp -f $< $(top_builddir)/bin/ugrep$(EXEEXT); \ cd $(top_builddir)/bin; \ rm -f ug$(EXEEXT); \ - $(LN_S) ugrep$(EXEEXT) ug$(EXEEXT) + cp -f ugrep$(EXEEXT) ug$(EXEEXT) @echo @echo "OK" @echo install-exec-hook: rm -f $(DESTDIR)$(bindir)/ug$(EXEEXT) - cd $(DESTDIR)$(bindir) && $(LN_S) ugrep$(EXEEXT) ug$(EXEEXT) + cd $(DESTDIR)$(bindir) && cp -f ugrep$(EXEEXT) ug$(EXEEXT) install-data-hook: mkdir -p $(DESTDIR)$(datadir)/ugrep && \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/Makefile.in new/ugrep-3.10.0/Makefile.in --- old/ugrep-3.9.7/Makefile.in 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/Makefile.in 2023-03-01 02:59:57.000000000 +0100 @@ -902,14 +902,14 @@ cp -f $< $(top_builddir)/bin/ugrep$(EXEEXT); \ cd $(top_builddir)/bin; \ rm -f ug$(EXEEXT); \ - $(LN_S) ugrep$(EXEEXT) ug$(EXEEXT) + cp -f ugrep$(EXEEXT) ug$(EXEEXT) @echo @echo "OK" @echo install-exec-hook: rm -f $(DESTDIR)$(bindir)/ug$(EXEEXT) - cd $(DESTDIR)$(bindir) && $(LN_S) ugrep$(EXEEXT) ug$(EXEEXT) + cd $(DESTDIR)$(bindir) && cp -f ugrep$(EXEEXT) ug$(EXEEXT) install-data-hook: mkdir -p $(DESTDIR)$(datadir)/ugrep && \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/README.md new/ugrep-3.10.0/README.md --- old/ugrep-3.9.7/README.md 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/README.md 2023-03-01 02:59:57.000000000 +0100 @@ -1,6 +1,6 @@ [![build status][travis-image]][travis-url] [![license][bsd-3-image]][bsd-3-url] -**ugrep v3.9 is now available: more features & even faster than before** +**ugrep v3.10 is now available: more features & even faster than before** Search for anything in everything... ultra fast @@ -398,9 +398,8 @@ - `--help` display build options After the build completes, copy `ugrep/bin/ugrep` and `ugrep/bin/ug` to a -convenient location, for example in your `~/bin` directory. - -You may want to install the `ugrep` and `ug` commands and man pages with: +convenient location, for example in your `~/bin` directory. Or, if you may want +to install the `ugrep` and `ug` commands and man pages: $ sudo make install @@ -1358,13 +1357,15 @@ Only the names of files not containing selected lines are written to standard output. Pathnames are listed once per file searched. If the standard input is searched, the string ``(standard input)'' - is written. + is written. If --tree is specified, outputs directories in a + tree-like format. -l, --files-with-matches Only the names of files containing selected lines are written to standard output. ugrep will only search a file until a match has been found, making searches potentially less expensive. Pathnames are listed once per file searched. If the standard input is - searched, the string ``(standard input)'' is written. + searched, the string ``(standard input)'' is written. If --tree is + specified, outputs directories in a tree-like format. -R, --dereference-recursive Recursively read all files under each directory. Follow all symbolic links, unlike -r. See also option --sort. @@ -3004,9 +3005,10 @@ ### Counting the number of matches with -c and -co -c, --count - Only a count of selected lines is written to standard output. - If -o or -u is specified, counts the number of patterns matched. - If -v is specified, counts the number of non-matching lines. + Only a count of selected lines is written to standard output. If + -o or -u is specified, counts the number of patterns matched. If + -v is specified, counts the number of non-matching lines. If + --tree is specified, outputs directories in a tree-like format. To count the number of lines in a file: @@ -3124,7 +3126,7 @@ and --line-buffered. --pretty When output is sent to a terminal, enables --color, --heading, -n, - --sort and -T when not explicitly disabled. + --sort, --tree and -T when not explicitly disabled. To change the color palette, set the `GREP_COLORS` environment variable or use `--colors=COLORS`. The value is a colon-separated list of ANSI SGR parameters @@ -3983,7 +3985,9 @@ --lines. --break - Adds a line break between results from different files. + Adds a line break between results from different files. This + option is enabled by --pretty when the output is sent to a + terminal. -C NUM, --context=NUM Output NUM lines of leading and trailing context surrounding each @@ -3995,7 +3999,8 @@ -c, --count Only a count of selected lines is written to standard output. If -o or -u is specified, counts the number of patterns matched. If - -v is specified, counts the number of non-matching lines. + -v is specified, counts the number of non-matching lines. If + --tree is specified, outputs directories in a tree-like format. --color[=WHEN], --colour[=WHEN] Mark up the matching text with the expression stored in the @@ -4207,7 +4212,8 @@ --heading, -+ Group matches per file. Adds a heading and a line break between - results from different files. + results from different files. This option is enabled by --pretty + when the output is sent to a terminal. --help [WHAT], -? [WHAT] Display a help message, specifically on WHAT when specified. In @@ -4322,14 +4328,16 @@ Only the names of files not containing selected lines are written to standard output. Pathnames are listed once per file searched. If the standard input is searched, the string ``(standard input)'' - is written. + is written. If --tree is specified, outputs directories in a + tree-like format. -l, --files-with-matches Only the names of files containing selected lines are written to standard output. ugrep will only search a file until a match has been found, making searches potentially less expensive. Pathnames are listed once per file searched. If the standard input is - searched, the string ``(standard input)'' is written. + searched, the string ``(standard input)'' is written. If --tree + is specified, outputs directories in a tree-like format. --label=LABEL Displays the LABEL value when input is read from standard input @@ -4443,7 +4451,7 @@ --pretty When output is sent to a terminal, enables --color, --heading, -n, - --sort and -T when not explicitly disabled. + --sort, --tree and -T when not explicitly disabled. -Q[DELAY], --query[=DELAY] Query mode: user interface to perform interactive searches. This @@ -4558,6 +4566,11 @@ Disables colors to mark up matches with TAG. END marks the end of a match if specified, otherwise TAG. The default is `___'. + --tree, -^ + Output directories with matching files in a tree-like format when + options -c, -l or -L are used. This option is enabled by --pretty + when the output is sent to a terminal. + -U, --binary Disables Unicode matching for binary file matching, forcing PATTERN to match bytes, not Unicode characters. For example, -U @@ -5237,7 +5250,7 @@ - ugrep 3.9.3 December 29, 2022 UGREP(1) + ugrep 3.10.0 February 28, 2023 UGREP(1) ð [Back to table of contents](#toc) @@ -5251,8 +5264,9 @@ has more features than the pattern syntax described below. For the patterns in common the syntax and meaning are the same. -Note that `\s` and inverted bracket lists `[^...]` are modified to prevent -matching newlines `\n` to replicate the behavior of grep. +Note that `\s` and inverted bracket lists `[^...]` are modified in **ugrep** to +prevent matching newlines `\n`. This modification is done to replicate the +behavior of grep. <a name="posix-syntax"/> Binary files old/ugrep-3.9.7/bin/win32/ugrep.exe and new/ugrep-3.10.0/bin/win32/ugrep.exe differ Binary files old/ugrep-3.9.7/bin/win64/ugrep.exe and new/ugrep-3.10.0/bin/win64/ugrep.exe differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/man/ugrep.1 new/ugrep-3.10.0/man/ugrep.1 --- old/ugrep-3.9.7/man/ugrep.1 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/man/ugrep.1 2023-03-01 02:59:57.000000000 +0100 @@ -1,4 +1,4 @@ -.TH UGREP "1" "December 29, 2022" "ugrep 3.9.3" "User Commands" +.TH UGREP "1" "February 28, 2023" "ugrep 3.10.0" "User Commands" .SH NAME \fBugrep\fR, \fBug\fR -- file pattern searcher .SH SYNOPSIS @@ -128,7 +128,9 @@ \fB\-\-andnot\fR, \fB\-\-not\fR, \fB\-\-files\fR and \fB\-\-lines\fR. .TP \fB\-\-break\fR -Adds a line break between results from different files. +Adds a line break between results from different files. This +option is enabled by \fB\-\-pretty\fR when the output is sent to a +terminal. .TP \fB\-C\fR \fINUM\fR, \fB\-\-context\fR=\fINUM\fR Output NUM lines of leading and trailing context surrounding each @@ -140,7 +142,8 @@ \fB\-c\fR, \fB\-\-count\fR Only a count of selected lines is written to standard output. If \fB\-o\fR or \fB\-u\fR is specified, counts the number of patterns matched. -If \fB\-v\fR is specified, counts the number of non\-matching lines. +If \fB\-v\fR is specified, counts the number of non\-matching lines. If +\fB\-\-tree\fR is specified, outputs directories in a tree\-like format. .TP \fB\-\-color\fR[=\fIWHEN\fR], \fB\-\-colour\fR[=\fIWHEN\fR] Mark up the matching text with the expression stored in the @@ -348,7 +351,8 @@ .TP \fB\-\-heading\fR, \fB\-+\fR Group matches per file. Adds a heading and a line break between -results from different files. +results from different files. This option is enabled by \fB\-\-pretty\fR +when the output is sent to a terminal. .TP \fB\-\-help\fR [\fIWHAT\fR], \fB\-?\fR [\fIWHAT\fR] Display a help message, specifically on WHAT when specified. @@ -461,14 +465,16 @@ Only the names of files not containing selected lines are written to standard output. Pathnames are listed once per file searched. If the standard input is searched, the string ``(standard input)'' -is written. +is written. If \fB\-\-tree\fR is specified, outputs directories in a +tree\-like format. .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. +searched, the string ``(standard input)'' is written. If \fB\-\-tree\fR is +specified, outputs directories in a tree\-like format. .TP \fB\-\-label\fR=\fILABEL\fR Displays the LABEL value when input is read from standard input @@ -580,7 +586,7 @@ .TP \fB\-\-pretty\fR When output is sent to a terminal, enables \fB\-\-color\fR, \fB\-\-heading\fR, \fB\-n\fR, -\fB\-\-sort\fR and \fB\-T\fR when not explicitly disabled. +\fB\-\-sort\fR, \fB\-\-tree\fR and \fB\-T\fR when not explicitly disabled. .TP \fB\-Q\fR[\fIDELAY\fR], \fB\-\-query\fR[=\fIDELAY\fR] Query mode: user interface to perform interactive searches. This @@ -694,6 +700,11 @@ Disables colors to mark up matches with TAG. END marks the end of a match if specified, otherwise TAG. The default is `___'. .TP +\fB\-\-tree\fR, \-^ +Output directories with matching files in a tree\-like format when +options \fB\-c\fR, \fB\-l\fR or \fB\-L\fR are used. This option is enabled by \fB\-\-pretty\fR +when the output is sent to a terminal. +.TP \fB\-U\fR, \fB\-\-binary\fR Disables Unicode matching for binary file matching, forcing PATTERN to match bytes, not Unicode characters. For example, \fB\-U\fR '\\xa3' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/src/flag.hpp new/ugrep-3.10.0/src/flag.hpp --- old/ugrep-3.9.7/src/flag.hpp 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/src/flag.hpp 2023-03-01 02:59:57.000000000 +0100 @@ -127,6 +127,7 @@ extern Flag flag_line_number; extern Flag flag_smart_case; extern Flag flag_text; +extern Flag flag_tree; extern Flag flag_ungroup; extern Sort flag_sort_key; extern Action flag_devices_action; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/src/output.cpp new/ugrep-3.10.0/src/output.cpp --- old/ugrep-3.9.7/src/output.cpp 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/src/output.cpp 2023-03-01 02:59:57.000000000 +0100 @@ -360,6 +360,129 @@ } } +// output the 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 + + if (flag_tree) + { + // acquire lock on output and to access global Tree::path and Tree::depth + acquire(); + + int up = 0; + + while (!Tree::path.empty() && Tree::path.compare(0, Tree::path.size(), pathname, Tree::path.size()) != 0) + { + Tree::path.pop_back(); + + size_t len = Tree::path.rfind(PATHSEPCHR); + if (len == std::string::npos) + Tree::path.clear(); + else + Tree::path.resize(len + 1); + + ++up; + --Tree::depth; + } + + if (up > 0) + { + for (int i = 0; i < Tree::depth; ++i) + str(Tree::bar); + while (--up > 0) + str(Tree::end); + nl(); + } + + const char *sep; + + while ((sep = strchr(pathname + Tree::path.size(), PATHSEPCHR)) != NULL) + { + 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); + + str(pathname + Tree::path.size(), sep - (pathname + Tree::path.size()) + 1); + + if (nul && Tree::depth == 0) + chr('\0'); + nl(); + + Tree::path.assign(pathname, sep - pathname + 1); + ++Tree::depth; + } + + 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); + + if (color_hl != NULL) + { + str(color_hl); + uri(color_wd); + uri(pathname); + str(color_st); + } + + if (nul && Tree::depth == 0) + chr('\0'); + + str(pathname + Tree::path.size()); + + if (nul && Tree::depth == 0) + chr('\0'); + + } + else + { + if (nul) + chr('\0'); + + str(color_fn); + + if (color_hl != NULL) + { + str(color_hl); + uri(color_wd); + uri(pathname); + str(color_st); + } + + if (nul) + chr('\0'); + + str(pathname); + + if (nul) + chr('\0'); + + if (color_hl != NULL) + { + str(color_hl); + str(color_st); + } + + } + + if (!partname.empty()) + { + chr('{'); + str(partname); + chr('}'); + } + + str(color_off); +} + // output "Binary file ... matches" void Output::binary_file_matches(const char *pathname, const std::string& partname) { @@ -452,6 +575,107 @@ return std::pair<const char*,size_t>(NULL, 0); } +// output format with option --format-begin and --format-end +void Output::format(const char *format, size_t matches) +{ + const char *sep = NULL; + size_t len = 0; + const char *s = format; + while (*s != '\0') + { + const char *a = NULL; + const char *t = s; + while (*s != '\0' && *s != '%') + ++s; + str(t, s - t); + if (*s == '\0' || *(s + 1) == '\0') + break; + ++s; + if (*s == '[') + { + a = ++s; + while (*s != '\0' && *s != ']') + ++s; + if (*s == '\0' || *(s + 1) == '\0') + break; + ++s; + } + int c = *s; + switch (c) + { + case 'T': + if (flag_initial_tab) + { + if (a) + str(a, s - a - 1); + chr('\t'); + } + break; + + case 'S': + if (matches > 1) + { + if (a) + str(a, s - a - 1); + if (sep != NULL) + str(sep, len); + else + str(flag_separator); + } + break; + + case '$': + sep = a; + len = s - a - 1; + break; + + case 't': + fputc('\t', output); + break; + + case 's': + if (sep != NULL) + str(sep, len); + else + str(flag_separator); + break; + + case '~': +#ifdef OS_WIN + chr('\r'); +#endif + chr('\n'); + break; + + case 'm': + num(matches); + break; + + case '<': + if (matches <= 1 && a) + str(a, s - a - 1); + break; + + case '>': + if (matches > 1 && a) + str(a, s - a - 1); + break; + + case ',': + case ':': + case ';': + case '|': + if (matches > 1) + chr(c); + break; + + default: + chr(c); + } + ++s; + } +} + // output formatted match with options --format, --format-open, --format-close void Output::format(const char *format, const char *& pathname, const std::string& partname, size_t matches, reflex::AbstractMatcher *matcher, bool body, bool next) { @@ -934,9 +1158,9 @@ break; case 'Z': - if (flag_fuzzy > 0) + if (flag_fuzzy > 0 && !flag_files_with_matches && !flag_count) { - // --Z: we used the fuzzy matcher to search, so a dynamic cast is fine + // -Z: we used the fuzzy matcher to search, so a dynamic cast is fine reflex::FuzzyMatcher *fuzzy_matcher = dynamic_cast<reflex::FuzzyMatcher*>(matcher); num(fuzzy_matcher->edits()); } @@ -1836,3 +2060,10 @@ } const char *Output::Dump::color_hex[4] = { match_ms, color_sl, match_mc, color_cx }; + +const char *Output::Tree::bar = "| "; +const char *Output::Tree::ptr = "|_ "; +const char *Output::Tree::end = "~ "; + +std::string Output::Tree::path; +int Output::Tree::depth; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/src/output.hpp new/ugrep-3.10.0/src/output.hpp --- old/ugrep-3.9.7/src/output.hpp 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/src/output.hpp 2023-03-01 02:59:57.000000000 +0100 @@ -70,9 +70,7 @@ public: // sync state to synchronize output produced by multiple threads, UNORDERED or ORDERED by slot number - class Sync { - - public: + struct Sync { enum class Mode { UNORDERED, ORDERED }; @@ -229,9 +227,7 @@ }; // hex dump state - class Dump { - - public: + struct Dump { // hex dump mode for color highlighting static constexpr short HEX_MATCH = 0; @@ -299,6 +295,18 @@ }; + // global directory tree state for output, protected by acquire() + struct Tree { + + static const char *bar; // fixed string to display a vertical line + static const char *ptr; // fixed string to display a vertical line and connector + static const char *end; // fixed string to display a vertical line ending + + static std::string path; // tree directory path buffer + static int depth; // tree directory depth + + }; + // constructor Output(FILE *file) : @@ -320,6 +328,7 @@ // destructor ~Output() { + flush(); if (lock_ != NULL) delete lock_; } @@ -710,9 +719,15 @@ // output the header part of the match, preceding the matched line void header(const char *& pathname, const std::string& partname, size_t lineno, reflex::AbstractMatcher *matcher, size_t byte_offset, const char *sep, bool newline); + // output the pathname header for --files_with_matches and --count + void header(const char *pathname, const std::string& partname); + // output "Binary file ... matches" void binary_file_matches(const char *pathname, const std::string& partname); + // output format with option --format-begin and --format-end + void format(const char *format, size_t matches); + // output formatted match with options --format, --format-open, --format-close void format(const char *format, const char *& pathname, const std::string& partname, size_t matches, reflex::AbstractMatcher *matcher, bool body, bool next); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/src/query.cpp new/ugrep-3.10.0/src/query.cpp --- old/ugrep-3.9.7/src/query.cpp 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/src/query.cpp 2023-03-01 02:59:57.000000000 +0100 @@ -3513,54 +3513,26 @@ // 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) { - size_t start = 0; - size_t pos = 0; size_t end = line.size(); - if (flag_files_with_matches || flag_count) - { - while (pos < end) - { - unsigned char c = line.at(pos); - - if (c != '\033') - break; - - while (++pos < end && !isalpha(line.at(pos))) - continue; - - ++pos; - } - - if (pos >= end) - return false; - - start = pos; - - while (pos < end && line.at(pos) != '\033') - ++pos; - } - else - { - if (end < 4 || line.front() != '\0') - return false; + if (end < 4 || line.front() != '\0') + return false; - pos = 1; + size_t pos = 1; - while (pos < end && line.at(pos) != '\0') - ++pos; + while (pos < end && line.at(pos) != '\0') + ++pos; - if (++pos >= end) - return false; + if (++pos >= end) + return false; - start = pos; + size_t start = pos; - while (pos < end && line.at(pos) != '\0') - ++pos; + while (pos < end && line.at(pos) != '\0') + ++pos; - if (pos == start || pos >= end) - return false; - } + if (pos == start || pos >= end) + return false; if (compare_dir) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/src/stats.cpp new/ugrep-3.10.0/src/stats.cpp --- old/ugrep-3.9.7/src/stats.cpp 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/src/stats.cpp 2023-03-01 02:59:57.000000000 +0100 @@ -124,7 +124,7 @@ fprintf(output, " --no-hidden (default)" NEWLINESTR); #endif for (auto& i : flag_ignore_files) - fprintf(output, " --ignore-files='%s'" NEWLINESTR, i.c_str()); + fprintf(output, " --ignore-files=\"%s\"" NEWLINESTR, i.c_str()); if (flag_min_count > 0) fprintf(output, " --min-count=%zu" NEWLINESTR, flag_min_count); if (flag_max_count > 0) @@ -136,22 +136,22 @@ for (auto& i : flag_file_magic) { if (!i.empty() && (i.front() == '!' || i.front() == '^')) - fprintf(output, " --file-magic='!%s' (negation)" NEWLINESTR, i.c_str() + 1); + fprintf(output, " --file-magic=\"!%s\" (negated)" NEWLINESTR, i.c_str() + 1); else - fprintf(output, " --file-magic='%s'" NEWLINESTR, i.c_str()); + fprintf(output, " --file-magic=\"%s\"" NEWLINESTR, i.c_str()); } for (auto& i : flag_include_fs) - fprintf(output, " --include-fs='%s'" NEWLINESTR, i.c_str()); + fprintf(output, " --include-fs=\"%s\"" NEWLINESTR, i.c_str()); for (auto& i : flag_exclude_fs) - fprintf(output, " --exclude-fs='%s'" NEWLINESTR, i.c_str()); + fprintf(output, " --exclude-fs=\"%s\"" NEWLINESTR, i.c_str()); for (auto& i : flag_all_include) - fprintf(output, " --include='%s'%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); + fprintf(output, " --include=\"%s\"%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); for (auto& i : flag_all_exclude) - fprintf(output, " --exclude='%s'%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); + fprintf(output, " --exclude=\"%s\"%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); for (auto& i : flag_all_include_dir) - fprintf(output, " --include-dir='%s'%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); + fprintf(output, " --include-dir=\"%s\"%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); for (auto& i : flag_all_exclude_dir) - fprintf(output, " --exclude-dir='%s'%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); + fprintf(output, " --exclude-dir=\"%s\"%s" NEWLINESTR, i.c_str(), i.front() == '!' ? " (negated)" : ""); } reflex::timer_type Stats::timer; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/src/ugrep.cpp new/ugrep-3.10.0/src/ugrep.cpp --- old/ugrep-3.9.7/src/ugrep.cpp 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/src/ugrep.cpp 2023-03-01 02:59:57.000000000 +0100 @@ -368,6 +368,7 @@ Flag flag_line_number; Flag flag_smart_case; Flag flag_text; +Flag flag_tree; Flag flag_ungroup; Sort flag_sort_key = Sort::NA; Action flag_devices_action = Action::UNSP; @@ -474,7 +475,6 @@ void strtopos2(const char *string, size_t& pos1, size_t& pos2, const char *message); size_t strtofuzzy(const char *string, const char *message); void import_globs(FILE *file, std::vector<std::string>& files, std::vector<std::string>& dirs); -void format(const char *format, size_t matches); void usage(const char *message, const char *arg = NULL, const char *valid = NULL); void help(std::ostream& out); void help(const char *what = NULL); @@ -695,7 +695,7 @@ } // start decompression thread and open new pipe, returns pipe or NULL on failure, this function is called by the main Grep thread - FILE *start(int ztstage, const char *pathname, FILE *file_in) + FILE *start(size_t ztstage, const char *pathname, FILE *file_in) { // return pipe FILE *pipe_in = NULL; @@ -1138,12 +1138,13 @@ // tar header fields name, size and prefix and make them \0-terminated by overwriting fields we do not use buf[100] = '\0'; const char *name = reinterpret_cast<const char*>(buf); + // path prefix is up to 155 bytes (ustar) or up to 131 bytes (gnutar) buf[345 + (is_ustar ? 155 : 131)] = '\0'; const char *prefix = reinterpret_cast<const char*>(buf + 345); - // check GNU tar extension with leading byte 0x80 (unsigned positive) or leading byte 0xff (negative) - size_t size = 0; + // check gnutar size with leading byte 0x80 (unsigned positive) or leading byte 0xff (negative) + uint64_t size = 0; if (buf[124] == 0x80) { // 11 byte big-endian size field without the leading 0x80 @@ -1192,7 +1193,7 @@ memmove(buf, buf + BLOCKSIZE, static_cast<size_t>(len)); // check if archived file meets selection criteria - size_t minlen = std::min(static_cast<size_t>(len), size); + size_t minlen = static_cast<size_t>(std::min(static_cast<uint64_t>(len), size)); // size_t is OK: len is streamsize but non-negative bool is_selected = select_matching(path.c_str(), buf, minlen, is_regular); // if extended headers are present @@ -1255,7 +1256,7 @@ while (len > 0 && !stop) { - size_t len_out = std::min(static_cast<size_t>(len), size); + size_t len_out = static_cast<size_t>(std::min(static_cast<uint64_t>(len), size)); // size_t is OK: len is streamsize but non-negative if (ok) { @@ -1270,7 +1271,7 @@ if (size == 0) { len -= len_out; - memmove(buf, buf + len_out, static_cast<size_t>(len)); + memmove(buf, buf + len_out, static_cast<size_t>(len)); // size_t is OK: len is streamsize but non-negative break; } @@ -1563,7 +1564,7 @@ while (len > 0 && !stop) { - size_t len_out = std::min(static_cast<size_t>(len), size); + size_t len_out = std::min(static_cast<size_t>(len), size); // size_t is OK: len is streamsize but non-negative if (ok) { @@ -4215,8 +4216,10 @@ # specifies background colors. A `+' qualifies a color as bright. A\n\ # foreground and a background color may be combined with font properties `n'\n\ # (normal), `f' (faint), `h' (highlight), `i' (invert), `u' (underline).\n\n"); + 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"); if (flag_view != NULL && *flag_view == '\0') fprintf(file, "view\n\n"); @@ -4224,16 +4227,23 @@ fprintf(file, "view=%s\n\n", flag_view); else fprintf(file, "no-view\n\n"); + fprintf(file, "# Enable/disable or specify a pager for terminal output, default: no-pager\n"); if (flag_pager != NULL) fprintf(file, "pager=%s\n\n", flag_pager); else fprintf(file, "no-pager\n\n"); + fprintf(file, "# Enable/disable pretty output to the terminal, default: no-pretty\n%s\n\n", flag_pretty ? "pretty" : "no-pretty"); - fprintf(file, "# Enable/disable headings for terminal output, default: no-heading\n%s\n\n", flag_heading.is_undefined() ? "# no-heading" : flag_heading ? "heading" : "no-heading"); - if (flag_break.is_defined()) - fprintf(file, "# Enable/disable break for terminal output\n%s\n\n", flag_break ? "break" : "no-break"); + if (flag_tree.is_defined() && flag_tree != flag_pretty) + fprintf(file, "# Enable/disable directory tree output for --files-with-matches and --count\n%s\n\n", flag_tree ? "tree" : "no-tree"); + + if (flag_heading.is_defined() && flag_heading != flag_pretty) + fprintf(file, "# Enable/disable headings\n%s\n\n", flag_heading.is_undefined() ? "# no-heading" : flag_heading ? "heading" : "no-heading"); + + if (flag_break.is_defined() && flag_break != flag_pretty) + fprintf(file, "# Enable/disable break after matching files\n%s\n\n", flag_break ? "break" : "no-break"); if (flag_line_number.is_defined() && flag_line_number != flag_pretty) fprintf(file, "# Enable/disable line numbers\n%s\n\n", flag_line_number ? "line-number" : "no-line-number"); @@ -4251,6 +4261,7 @@ fprintf(file, "# Hex output\nhex\n\n"); else if (strcmp(flag_binary_files, "with-hex") == 0) fprintf(file, "# Output with hex for binary matches\nwith-hex\n\n"); + if (flag_hexdump != NULL) fprintf(file, "# Hex dump (columns, no space breaks, no character column, no hex spacing)\nhexdump=%s\n\n", flag_hexdump); @@ -4269,6 +4280,7 @@ if (flag_before_context > 0) fprintf(file, "# Display lines before context\nbefore-context=%zu\n\n", flag_before_context); } + if (flag_group_separator == NULL) fprintf(file, "# Disable group separator for contexts\nno-group-separator\n\n"); else if (strcmp(flag_group_separator, "--") != 0) @@ -4729,6 +4741,8 @@ flag_smart_case = false; else if (strcmp(arg, "no-sort") == 0) flag_sort = NULL; + else if (strcmp(arg, "no-tree") == 0) + flag_tree = false; else if (strcmp(arg, "no-stats") == 0) flag_stats = NULL; else if (strcmp(arg, "no-ungroup") == 0) @@ -4740,7 +4754,7 @@ else if (strcmp(arg, "neg-regexp") == 0) usage("missing argument for --", arg); else - usage("invalid option --", arg, "--neg-regexp, --not, --no-any-line, --no-binary, --no-bool, --no-break, --no-byte-offset, --no-color, --no-confirm, --no-decompress, --no-dereference, --no-dotall, --no-empty, --no-filename, --no-group-separator, --no-heading, --no-hidden, --no-ignore-binary, --no-ignore-case, --no-ignore-files --no-initial-tab, --no-invert-match, --no-line-number, --no-only-line-number, --no-only-matching, --no-messages, --no-mmap, --no-pager, --no-pretty, --no-smart-case, --no-sort, --no-stats, --no-ungroup, --no-view or --null"); + usage("invalid option --", arg, "--neg-regexp, --not, --no-any-line, --no-binary, --no-bool, --no-break, --no-byte-offset, --no-color, --no-confirm, --no-decompress, --no-dereference, --no-dotall, --no-empty, --no-filename, --no-group-separator, --no-heading, --no-hidden, --no-ignore-binary, --no-ignore-case, --no-ignore-files --no-initial-tab, --no-invert-match, --no-line-number, --no-only-line-number, --no-only-matching, --no-messages, --no-mmap, --no-pager, --no-pretty, --no-smart-case, --no-sort, --no-stats, --no-tree, --no-ungroup, --no-view or --null"); break; case 'o': @@ -4829,8 +4843,10 @@ flag_tag = arg + 4; else if (strcmp(arg, "text") == 0) flag_binary_files = "text"; + else if (strcmp(arg, "tree") == 0) + flag_tree = true; else - usage("invalid option --", arg, "--tabs, --tag or --text"); + usage("invalid option --", arg, "--tabs, --tag, --text or --tree"); break; case 'u': @@ -5252,6 +5268,10 @@ flag_bool = true; break; + case '^': + flag_tree = true; + break; + case '+': flag_heading = true; break; @@ -5585,6 +5605,10 @@ if (flag_query > 0) flag_pager = NULL; + // --tree: require sort to produce directory tree + if (flag_tree && flag_sort == NULL) + flag_sort = "name"; + // check TTY info and set colors (warnings and errors may occur from here on) terminal(); @@ -6362,7 +6386,7 @@ { if (flag_pretty) { - // --pretty: if output is to a TTY then enable --color, --heading, -T, -n, and --sort + // --pretty: if output is to a TTY then enable --color, --heading, -T, -n, --sort and --tree // enable --color if (flag_apply_color == NULL) @@ -6383,6 +6407,10 @@ // enable --sort=name if no --sort specified if (flag_sort == NULL) flag_sort = "name"; + + // enable --tree + if (flag_tree.is_undefined()) + flag_tree = true; } else if (flag_apply_color != NULL) { @@ -6426,6 +6454,22 @@ } } +#ifndef OS_WIN + + if (flag_tree && (flag_files_with_matches || flag_files_without_match || flag_count)) + { + const char *lang = getenv("LANG"); + + if (lang != NULL && strstr(lang, "UTF-8")) + { + Output::Tree::bar = "â "; + Output::Tree::ptr = "â°â´"; + Output::Tree::end = "â "; + } + } + +#endif + // --color: (re)set flag_apply_color depending on color_term and TTY output if (flag_apply_color != NULL) { @@ -6595,6 +6639,10 @@ // reset stats Stats::reset(); + // --tree: reset directory tree output + Output::Tree::path.clear(); + Output::Tree::depth = 0; + // populate the combined all-include and all-exclude flag_all_include = flag_include; flag_all_include_dir = flag_include_dir; @@ -6786,16 +6834,16 @@ flag_sort_key = Sort::BEST; else if (strcmp(sort_by, "size") == 0) flag_sort_key = Sort::SIZE; - else if (strcmp(sort_by, "used") == 0) + else if (strcmp(sort_by, "used") == 0 || strcmp(sort_by, "atime") == 0) flag_sort_key = Sort::USED; - else if (strcmp(sort_by, "changed") == 0) + else if (strcmp(sort_by, "changed") == 0 || strcmp(sort_by, "mtime") == 0) flag_sort_key = Sort::CHANGED; - else if (strcmp(sort_by, "created") == 0) + else if (strcmp(sort_by, "created") == 0 || strcmp(sort_by, "ctime") == 0) flag_sort_key = Sort::CREATED; else if (strcmp(sort_by, "list") == 0) flag_sort_key = Sort::LIST; else - usage("invalid argument --sort=KEY, valid arguments are 'name', 'best', 'size', 'used', 'changed', 'created', 'list', 'rname', 'rbest', 'rsize', 'rused', 'rchanged', 'rcreated' and 'rlist'"); + usage("invalid argument --sort=KEY, valid arguments are 'name', 'best', 'size', 'used' ('atime'), 'changed' ('mtime'), 'created' ('ctime'), 'list', 'rname', 'rbest', 'rsize', 'rused' ('ratime'), 'rchanged' ('rmtime'), 'rcreated' ('rctime') and 'rlist'"); } // add PATTERN to the CNF @@ -7177,7 +7225,7 @@ // --format-begin if (flag_format_begin != NULL) - format(flag_format_begin, 0); + Output(output).format(flag_format_begin, 0); size_t nodes = 0; size_t edges = 0; @@ -7437,9 +7485,18 @@ words_time = static_cast<size_t>(pattern.words_time()); } + // --tree with -l or -c + if (flag_tree && (flag_files_with_matches || flag_count)) + { + Output out(output); + for (int i = 1; i < Output::Tree::depth; ++i) + out.str(Output::Tree::end); + out.nl(); + } + // --format-end if (flag_format_end != NULL) - format(flag_format_end, Stats::found_parts()); + Output(output).format(flag_format_end, Stats::found_parts()); // --stats: display stats when we're done if (flag_stats != NULL) @@ -8561,27 +8618,7 @@ } else { - out.str(color_fn); - if (color_hl != NULL) - { - out.str(color_hl); - out.uri(color_wd); - out.uri(pathname); - out.str(color_st); - } - out.str(pathname); - if (color_hl != NULL) - { - out.str(color_hl); - out.str(color_st); - } - if (!partname.empty()) - { - out.chr('{'); - out.str(partname); - out.chr('}'); - } - out.str(color_off); + out.header(pathname, partname); if (flag_null) out.chr('\0'); @@ -8713,27 +8750,7 @@ { if (flag_with_filename || !partname.empty()) { - out.str(color_fn); - if (color_hl != NULL) - { - out.str(color_hl); - out.uri(color_wd); - out.uri(pathname); - out.str(color_st); - } - out.str(pathname); - if (color_hl != NULL) - { - out.str(color_hl); - out.str(color_st); - } - if (!partname.empty()) - { - out.chr('{'); - out.str(partname); - out.chr('}'); - } - out.str(color_off); + out.header(pathname, partname); if (flag_null) { @@ -11334,107 +11351,6 @@ } } -// display format with option --format-begin and --format-end -void format(const char *format, size_t matches) -{ - const char *sep = NULL; - size_t len = 0; - const char *s = format; - while (*s != '\0') - { - const char *a = NULL; - const char *t = s; - while (*s != '\0' && *s != '%') - ++s; - fwrite(t, 1, s - t, output); - if (*s == '\0' || *(s + 1) == '\0') - break; - ++s; - if (*s == '[') - { - a = ++s; - while (*s != '\0' && *s != ']') - ++s; - if (*s == '\0' || *(s + 1) == '\0') - break; - ++s; - } - int c = *s; - switch (c) - { - case 'T': - if (flag_initial_tab) - { - if (a) - fwrite(a, 1, s - a - 1, output); - fputc('\t', output); - } - break; - - case 'S': - if (matches > 1) - { - if (a) - fwrite(a, 1, s - a - 1, output); - if (sep != NULL) - fwrite(sep, 1, len, output); - else - fputs(flag_separator, output); - } - break; - - case '$': - sep = a; - len = s - a - 1; - break; - - case 't': - fputc('\t', output); - break; - - case 's': - if (sep != NULL) - fwrite(sep, 1, len, output); - else - fputs(flag_separator, output); - break; - - case '~': -#ifdef OS_WIN - fputc('\r', output); -#endif - fputc('\n', output); - break; - - case 'm': - fprintf(output, "%zu", matches); - break; - - case '<': - if (matches <= 1 && a) - fwrite(a, 1, s - a - 1, output); - break; - - case '>': - if (matches > 1 && a) - fwrite(a, 1, s - a - 1, output); - break; - - case ',': - case ':': - case ';': - case '|': - if (matches > 1) - fputc(c, output); - break; - - default: - fputc(c, output); - } - ++s; - } -} - // trim white space from either end of the line void trim(std::string& line) { @@ -11823,7 +11739,9 @@ displays the search patterns applied. See also options --and,\n\ --andnot, --not, --files and --lines.\n\ --break\n\ - Adds a line break between results from different files.\n\ + Adds a line break between results from different files. This\n\ + option is enabled by --pretty when the output is sent to a\n\ + terminal.\n\ -C NUM, --context=NUM\n\ Output NUM lines of leading and trailing context surrounding each\n\ matching line. Places a --group-separator between contiguous\n\ @@ -11833,7 +11751,8 @@ -c, --count\n\ Only a count of selected lines is written to standard output.\n\ If -o or -u is specified, counts the number of patterns matched.\n\ - If -v is specified, counts the number of non-matching lines.\n\ + If -v is specified, counts the number of non-matching lines. If\n\ + --tree is specified, outputs directories in a tree-like format.\n\ --color[=WHEN], --colour[=WHEN]\n\ Mark up the matching text with the expression stored in the\n\ GREP_COLOR or GREP_COLORS environment variable. WHEN can be\n\ @@ -12031,7 +11950,8 @@ when there is only one file (or only standard input) to search.\n\ --heading, -+\n\ Group matches per file. Adds a heading and a line break between\n\ - results from different files.\n\ + results from different files. This option is enabled by --pretty\n\ + when the output is sent to a terminal.\n\ --help [WHAT], -? [WHAT]\n\ Display a help message, specifically on WHAT when specified.\n\ In addition, `--help format' displays an overview of FORMAT fields,\n\ @@ -12136,13 +12056,15 @@ Only the names of files not containing selected lines are written\n\ to standard output. Pathnames are listed once per file searched.\n\ If the standard input is searched, the string ``(standard input)''\n\ - is written.\n\ + is written. If --tree is specified, outputs directories in a\n\ + tree-like format.\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\ + searched, the string ``(standard input)'' is written. If --tree is\n\ + specified, outputs directories in a tree-like format.\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\ @@ -12243,7 +12165,7 @@ and --line-buffered.\n\ --pretty\n\ When output is sent to a terminal, enables --color, --heading, -n,\n\ - --sort and -T when not explicitly disabled.\n\ + --sort, --tree and -T when not explicitly disabled.\n\ -Q[DELAY], --query[=DELAY]\n\ Query mode: user interface to perform interactive searches. This\n\ mode requires an ANSI capable terminal. An optional DELAY argument\n\ @@ -12329,6 +12251,10 @@ --tag[=TAG[,END]]\n\ Disables colors to mark up matches with TAG. END marks the end of\n\ a match if specified, otherwise TAG. The default is `___'.\n\ + --tree, -^\n\ + Output directories with matching files in a tree-like format when\n\ + options -c, -l or -L are used. This option is enabled by --pretty\n\ + when the output is sent to a terminal.\n\ -U, --binary\n\ Disables Unicode matching for binary file matching, forcing PATTERN\n\ to match bytes, not Unicode characters. For example, -U '\\xa3'\n\ @@ -12572,9 +12498,9 @@ %t tab %[name]d named capture byte size\n\ %T %[...]T ... + tab, if -T %[name]e named capture end offset\n\ %u unique lines, unless -u %[n|...]# capture n,... that matched\n\ - %v matching pattern, as CSV %[n|...]b cpature n,... byte offset\n\ - %V matching line, as CSV %[n|...]d cpature n,... byte size\n\ - %w match width in wide chars %[n|...]e cpature n,... end offset\n\ + %v matching pattern, as CSV %[n|...]b capture n,... byte offset\n\ + %V matching line, as CSV %[n|...]d capture n,... byte size\n\ + %w match width in wide chars %[n|...]e capture n,... end offset\n\ %x matching pattern, as XML %g capture number or name\n\ %X matching line, as XML %G all capture numbers/names\n\ %z path in archive %[t|...]g text t indexed by capture\n\ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ugrep-3.9.7/src/ugrep.hpp new/ugrep-3.10.0/src/ugrep.hpp --- old/ugrep-3.9.7/src/ugrep.hpp 2023-01-31 17:06:32.000000000 +0100 +++ new/ugrep-3.10.0/src/ugrep.hpp 2023-03-01 02:59:57.000000000 +0100 @@ -38,7 +38,7 @@ #define UGREP_HPP // ugrep version -#define UGREP_VERSION "3.9.7" +#define UGREP_VERSION "3.10.0" // disable mmap because mmap is almost always slower than the file reading speed improvements since 3.0.0 #define WITH_NO_MMAP