@ 260915.5

260915.2 · 1739556b7485df48bde0c256ae9c76096b3f537b

update matching mode flags logic

- make mode flags -E, -F, -G, -P respect order on the command line to permit overriding (like BSD grep)
- warn when used in a config file #562

reading declarations

src/cnf.cpp

@@ -94,7 +94,7 @@ void CNF::OpTree::parse3(const char *& pattern)    bool parens = false; -  if (*pattern == '(' && (flag_fixed_strings || pattern[1] != '?'))+  if (*pattern == '(' && (flag_mode == Mode::FIXED || pattern[1] != '?'))   {     // check if matching ending ) is followed by spacing or EOS or |, then it is OK to parse '(' <parse1> ')' and convert to CNF     int level = 0;@@ -120,7 +120,7 @@ void CNF::OpTree::parse3(const char *& pattern)          --level;       }-      else if (*lookahead == '[' && !flag_fixed_strings)+      else if (*lookahead == '[' && flag_mode != Mode::FIXED)       {         // skip [...]         ++lookahead;@@ -196,7 +196,7 @@ void CNF::OpTree::parse3(const char *& pattern)         }          // if not -x then quote the string with \Q and \E-        if (!flag_fixed_strings)+        if (flag_mode != Mode::FIXED)           quote(quoted);          regex.append(quoted);@@ -206,7 +206,7 @@ void CNF::OpTree::parse3(const char *& pattern)          pattern  = lookahead;       }-      else if (*lookahead == '[' && !flag_fixed_strings)+      else if (*lookahead == '[' && flag_mode != Mode::FIXED)       {         // skip [...]         ++lookahead;@@ -256,7 +256,7 @@ void CNF::OpTree::parse3(const char *& pattern)      if (flag_line_regexp && regex.empty())       regex.assign("^$");-    else if (flag_fixed_strings)+    else if (flag_mode == Mode::FIXED)       quote(regex);      // -w and -x@@ -418,7 +418,7 @@ void CNF::OpTree::convert(Terms& terms)       {         // pop unused ending '|' (or BRE '\|')         terms.pop_back();-        if (flag_basic_regexp)+        if (flag_mode == Mode::BRE)           terms.pop_back();       }     }@@ -462,7 +462,7 @@ void CNF::OpTree::add_to(Terms& terms) const     else if (regex.empty())       term.front()->clear(); // empty pattern means anything matches     else-      term.front()->append(flag_basic_regexp ? "\\|" : "|").append(regex);+      term.front()->append(flag_mode == Mode::BRE ? "\\|" : "|").append(regex);      // empty pattern means anything matches     if (term.front()->empty())@@ -492,7 +492,7 @@ void CNF::new_pattern(PATTERN mask, const char *pattern)     std::string spattern(pattern);      // -F-    if (flag_fixed_strings)+    if (flag_mode == Mode::FIXED)       quote(spattern);      // -w and -x@@ -526,7 +526,7 @@ void CNF::new_pattern(PATTERN mask, const char *pattern)       else if (spattern.empty())         term.front()->clear(); // empty pattern means anything matches       else-        term.front()->append(flag_basic_regexp ? "\\|" : "|").append(spattern);+        term.front()->append(flag_mode == Mode::BRE ? "\\|" : "|").append(spattern);        // empty pattern means anything matches       if (term.front()->empty())@@ -566,7 +566,7 @@ void CNF::split()   if (flag_bool)     return; -  const char *sep = flag_fixed_strings ? "\\E|\\Q" : flag_basic_regexp ? "\\|" : "|";+  const char *sep = flag_mode == Mode::FIXED ? "\\E|\\Q" : flag_mode == Mode::BRE ? "\\|" : "|";    for (auto& i : terms)   {@@ -644,7 +644,7 @@ void CNF::report(FILE *output) const      bool or_sep = false; -    if (flag_fixed_strings)+    if (flag_mode == Mode::FIXED)       fprintf(output, "a string in ");     else       fprintf(output, "a pattern in ");@@ -731,7 +731,7 @@ void CNF::report(FILE *output) const std::string CNF::adjoin() const {   std::string adjoined;-  const char *sep = flag_basic_regexp ? "\\|" : "|";+  const char *sep = flag_mode == Mode::BRE ? "\\|" : "|";    if (flag_files)   {@@ -765,7 +765,7 @@ std::string CNF::adjoin() const   {     // pop unused ending '|' (or BRE '\|')     adjoined.pop_back();-    if (flag_basic_regexp)+    if (flag_mode == Mode::BRE)       adjoined.pop_back();   } 

src/cnf.hpp

@@ -178,14 +178,14 @@ class CNF {     {       // -x: make the regex line-anchored       // -G requires \( \) instead of ( )-      const char *xleft = flag_basic_regexp ? "^\\(" : "^(?:";-      const char *xright = flag_basic_regexp ? "\\)$" : ")$";+      const char *xleft = flag_mode == Mode::BRE ? "^\\(" : "^(?:";+      const char *xright = flag_mode == Mode::BRE ? "\\)$" : ")$";       pattern.insert(0, xleft).append(xright);     }     else if (flag_word_regexp)     {       // -w: make the regex word-anchored (or implicitly done with matcher option W instead of \< and \>)-      if (flag_perl_regexp)+      if (flag_mode == Mode::PERL)       {         // -P requires (?<!\w) (?!\w) instead of \< and \> #if defined(HAVE_PCRE2)

src/flag.hpp

@@ -68,10 +68,12 @@ enum class Sort { NA, NAME, BEST, SIZE, USED, CHANGED, CREATED, LIST }; // -D, --devices and -d, --directories enum class Action { UNSP, SKIP, READ, RECURSE }; +// -E (default), -G, -F, -P matching modes+enum class Mode { UNSP, ERE, BRE, FIXED, PERL };+ // ugrep command-line options extern bool flag_all_threads; // internal flag extern bool flag_any_line;-extern bool flag_basic_regexp; extern bool flag_best_match; extern bool flag_bool; extern bool flag_color_term; // internal flag@@ -85,7 +87,6 @@ extern bool flag_dereference_files; extern bool flag_files; extern bool flag_files_with_matches; extern bool flag_files_without_match;-extern bool flag_fixed_strings; extern bool flag_glob_ignore_case; extern bool flag_grep; // internal flag extern bool flag_hex;@@ -110,7 +111,6 @@ extern bool flag_null; extern bool flag_null_data; extern bool flag_only_line_number; extern bool flag_only_matching;-extern bool flag_perl_regexp; extern bool flag_query; extern bool flag_quiet; extern bool flag_sort_rev; // internal flag@@ -141,6 +141,7 @@ extern Flag flag_ungroup; extern Sort flag_sort_key; // internal flag extern Action flag_devices_action; // internal flag extern Action flag_directories_action; // internal flag+extern Mode flag_mode; extern size_t flag_after_context; extern size_t flag_before_context; extern size_t flag_delay;

src/query.cpp

@@ -190,7 +190,7 @@ void Query::display(int col, int len)           ++look;       }     }-    else if (!flag_fixed_strings)+    else if (flag_mode != ::Mode::FIXED)     {       for (const char *look = line_; look < ptr; ++look)       {@@ -319,7 +319,7 @@ void Query::display(int col, int len)             ptr = ++next + 1;           }         }-        else if (!flag_fixed_strings)+        else if (flag_mode != ::Mode::FIXED)         {           if (ch == '[' && !list && !literal && !braced)           {@@ -387,7 +387,7 @@ void Query::display(int col, int len)           }           else if (strchr("$()*+.?^|", ch) != NULL && !list && !literal && !braced)           {-            if (!flag_basic_regexp || strchr("()+?|", ch) == NULL)+            if (flag_mode != ::Mode::BRE || strchr("()+?|", ch) == NULL)             {               Screen::put(ptr, next - ptr);               Screen::normal();@@ -420,7 +420,7 @@ void Query::display(int col, int len)             if (prev == ' ' || prev == '|' || prev == '(' || prev == -1)             {               if (ch == '-' ||-                  (flag_fixed_strings && (ch == '(' || ch == ')' || ch == '|')) ||+                  (flag_mode == ::Mode::FIXED && (ch == '(' || ch == ')' || ch == '|')) ||                   strncmp(next, "AND ", 4) == 0 ||                   strncmp(next, "OR ", 3) == 0 ||                   strncmp(next, "NOT ", 4) == 0)@@ -438,7 +438,7 @@ void Query::display(int col, int len)                 ch = ' ';               }             }-            else if (flag_fixed_strings &&+            else if (flag_mode == ::Mode::FIXED &&                 (ch == '|' ||                  (ch == ')' && (next + 1 >= end || next[1] == ' ' || next[1] == '|' || next[1] == ')'))))             {@@ -1949,7 +1949,7 @@ void Query::execute(int pipe_fd)         error_ -= 4 + flag_ignore_case + flag_dotall + flag_free_space;        // subtract 2 for -F-      if (flag_fixed_strings && error_ >= 2)+      if (flag_mode == ::Mode::FIXED && error_ >= 2)         error_ -= 2;        // subtract 2 or 3 for -x or -w@@ -3936,8 +3936,8 @@ void Query::get_flags()   flags_[2].flag = flag_byte_offset;   flags_[3].flag = flag_after_context > 0 && flag_before_context > 0;   flags_[4].flag = flag_count;-  flags_[5].flag = flag_fixed_strings;-  flags_[6].flag = flag_basic_regexp;+  flags_[5].flag = flag_mode == ::Mode::FIXED;+  flags_[6].flag = flag_mode == ::Mode::BRE;   flags_[7].flag = !globs_.empty();   flags_[8].flag = flag_with_filename;   flags_[9].flag = flag_no_filename;@@ -3948,7 +3948,7 @@ void Query::get_flags()   flags_[14].flag = flag_files_with_matches;   flags_[15].flag = flag_line_number;   flags_[16].flag = flag_only_matching;-  flags_[17].flag = flag_perl_regexp;+  flags_[17].flag = flag_mode == ::Mode::PERL;   flags_[18].flag = flag_directories_action == Action::RECURSE && flag_dereference;   flags_[19].flag = flag_directories_action == Action::RECURSE && !flag_dereference;   flags_[20].flag = flag_initial_tab;@@ -4047,8 +4047,7 @@ void Query::set_flags()   }   flag_byte_offset = flags_[2].flag;   flag_count = flags_[4].flag;-  flag_fixed_strings = flags_[5].flag;-  flag_basic_regexp = flags_[6].flag;+  flag_mode = flags_[6].flag ? ::Mode::BRE : flags_[5].flag ? ::Mode::FIXED : flags_[17].flag ? ::Mode::PERL : ::Mode::ERE;   flag_glob.clear();   flag_iglob.clear();   if (globbing_)@@ -4065,7 +4064,6 @@ void Query::set_flags()   flag_files_with_matches = flags_[14].flag;   flag_line_number = flags_[15].flag;   flag_only_matching = flags_[16].flag;-  flag_perl_regexp = flags_[17].flag;   if (flags_[18].flag)     flag_directories_action = Action::RECURSE, flag_dereference = true;   else if (flags_[19].flag)

src/stats.cpp

@@ -100,16 +100,18 @@ void Stats::report(FILE *output)     for (const auto& i : flag_config_files)       fprintf(output, "    using %s" NEWLINESTR, i.c_str());   }+  if (flag_fuzzy > 0)+    fprintf(output, "  --fuzzy" NEWLINESTR);   if (flag_bool)     fprintf(output, "  --bool %s" NEWLINESTR, (flag_files ? "--files" : "--lines"));-  if (flag_basic_regexp)+  if (flag_mode == Mode::BRE)     fprintf(output, "  --basic-regexp" NEWLINESTR);-  else if (flag_fixed_strings)+  else if (flag_mode == Mode::FIXED)     fprintf(output, "  --fixed-strings" NEWLINESTR);-  else if (flag_fuzzy > 0)-    fprintf(output, "  --fuzzy" NEWLINESTR);-  else if (flag_perl_regexp)+  else if (flag_mode == Mode::PERL)     fprintf(output, "  --perl-regexp" NEWLINESTR);+  else+    fprintf(output, "  --extended-regexp" NEWLINESTR);   if (flag_decompress)     fprintf(output, "  --decompress --zmax=%zu" NEWLINESTR, flag_zmax);   if (flag_min_depth > 0 && flag_max_depth > 0)

src/ugrep.cpp

@@ -303,7 +303,6 @@ inline uint64_t fsid_to_uint64(fsid_t& fsid) // ugrep command-line options bool flag_all_threads              = false; bool flag_any_line                 = false;-bool flag_basic_regexp             = false; bool flag_best_match               = false; bool flag_bool                     = false; bool flag_color_term               = false;@@ -317,7 +316,6 @@ bool flag_dereference_files        = false; bool flag_files                    = false; bool flag_files_with_matches       = false; bool flag_files_without_match      = false;-bool flag_fixed_strings            = false; bool flag_glob_ignore_case         = false; bool flag_grep                     = false; bool flag_hex                      = false;@@ -342,7 +340,6 @@ bool flag_null                     = false; bool flag_null_data                = false; bool flag_only_line_number         = false; bool flag_only_matching            = false;-bool flag_perl_regexp              = false; bool flag_query                    = false; bool flag_quiet                    = false; bool flag_sort_rev                 = false;@@ -373,6 +370,7 @@ Flag flag_ungroup; Sort flag_sort_key                 = Sort::NA; Action flag_devices_action         = Action::UNSP; Action flag_directories_action     = Action::UNSP;+Mode flag_mode                     = Mode::UNSP; size_t flag_after_context          = 0; size_t flag_before_context         = 0; size_t flag_delay                  = DEFAULT_QUERY_DELAY;@@ -3971,7 +3969,7 @@ struct Grep {  #if !defined(HAVE_PCRE2) && defined(HAVE_BOOST_REGEX)       // buffer all input to work around Boost.Regex partial matching bug, but this may throw std::bad_alloc if the file is too large-      if (flag_perl_regexp)+      if (flag_mode == Mode::PERL)       {         matcher->buffer();       }@@ -4937,9 +4935,14 @@ static void load_config(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_          options(pattern_args, 2, args); -        if (Static::warnings > 0)+        if (Static::warnings > 0 ||+            flag_bool || flag_fuzzy || flag_mode != Mode::UNSP ||+            flag_any_line || flag_count || flag_files_with_matches || flag_files_without_match ||+            flag_invert_match || flag_line_regexp || flag_only_line_number || flag_only_matching ||+            flag_replace || flag_text || flag_word_regexp)         {-          std::cerr << "ugrep: error in " << config_file << " at line " << lineno << '\n';+          // reject invalid options and options that don't make sense for config+          std::cerr << "ugrep: error in config file " << config_file << " at line " << lineno << '\n';           errors = true;         }         else if (line.compare(0, 8, "--config") == 0)@@ -4947,7 +4950,7 @@ static void load_config(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_           // parse a config file, but do not recurse more than one level deep           if (recurse)           {-            std::cerr << "ugrep: recursive configuration in " << config_file << " at line " << lineno << '\n';+            std::cerr << "ugrep: error in config file " << config_file << " recursion at line " << lineno << '\n';             errors = true;           }           else@@ -4971,19 +4974,9 @@ static void load_config(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_       ++lineno;     } -    // clear flags that don't make sense for config, clashing and confusing to allow-    flag_any_line = false;-    flag_basic_regexp = false;-    flag_bool = false;-    flag_count = false;+    // silently ignore some options that don't make sense for config, clashing and confusing to allow     flag_file.clear();-    flag_files_with_matches = false;-    flag_files_without_match = false;-    flag_fixed_strings = false;     flag_from.clear();-    flag_fuzzy = 0;-    flag_invert_match = false;-    flag_line_regexp = false;     flag_match = false;     flag_max_count = 0;     flag_max_files = 0;@@ -4991,17 +4984,11 @@ static void load_config(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_     flag_max_size = 0;     flag_min_line = 0;     flag_min_size = 0;-    flag_only_line_number = false;-    flag_only_matching = false;     flag_pager = NULL;-    flag_perl_regexp = false;     flag_query = false;     flag_quiet = false;     flag_regexp.clear();-    flag_replace = NULL;     flag_save_config = NULL;-    flag_text = false;-    flag_word_regexp = false;     if (wdir)     {       flag_view = NULL;@@ -5351,7 +5338,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a                case 'b':                 if (strcmp(arg, "basic-regexp") == 0)-                  flag_basic_regexp = true;+                  flag_mode = Mode::BRE;                 else if (strcmp(arg, "before-context") == 0) // legacy form --before-context NUM                   flag_before_context = strtonum(getloptarg(argc, argv, "", i), "invalid argument --before-context=");                 else if (strncmp(arg, "before-context=", 15) == 0)@@ -5462,7 +5449,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a                 else if (strncmp(arg, "exclude-fs=", 11) == 0)                   flag_exclude_fs.emplace_back(getloptarg(argc, argv, arg + 11, i));                 else if (strcmp(arg, "extended-regexp") == 0)-                  flag_basic_regexp = false;+                  flag_mode = Mode::ERE;                 else if (strcmp(arg, "encoding") == 0)                   usage("missing argument for --", arg);                 else@@ -5487,7 +5474,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a                 else if (strcmp(arg, "files-without-match") == 0)                   flag_files_without_match = true;                 else if (strcmp(arg, "fixed-strings") == 0)-                  flag_fixed_strings = true;+                  flag_mode = Mode::FIXED;                 else if (strncmp(arg, "filter=", 7) == 0)                   flag_filter.append(flag_filter.empty() ? "" : ",").append(getloptarg(argc, argv, arg + 7, i));                 else if (strncmp(arg, "filter-magic-label=", 19) == 0)@@ -5803,7 +5790,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a                 else if (strcmp(arg, "passthru") == 0)                   flag_any_line = true;                 else if (strcmp(arg, "perl-regexp") == 0)-                  flag_perl_regexp = true;+                  flag_mode = Mode::PERL;                 else if (strcmp(arg, "pretty") == 0)                   flag_pretty = Static::AUTO;                 else if (strncmp(arg, "pretty=", 7) == 0)@@ -5980,7 +5967,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a             break;            case 'E':-            flag_basic_regexp = false;+            flag_mode = Mode::ERE;             break;            case 'e':@@ -5989,7 +5976,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a             break;            case 'F':-            flag_fixed_strings = true;+            flag_mode = Mode::FIXED;             break;            case 'f':@@ -5998,7 +5985,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a             break;            case 'G':-            flag_basic_regexp = true;+            flag_mode = Mode::BRE;             break;            case 'g':@@ -6077,7 +6064,7 @@ void options(std::list<std::pair<CNF::PATTERN,const char*>>& pattern_args, int a             break;            case 'P':-            flag_perl_regexp = true;+            flag_mode = Mode::PERL;             break;            case 'p':@@ -6449,7 +6436,7 @@ void init(int argc, const char **argv)   else if (strncmp(program, "grep", len) == 0)   {     // the 'grep' command is equivalent to 'ugrep --grep -G -. --sort'-    flag_basic_regexp = true;+    flag_mode = Mode::BRE;     flag_grep = true;     flag_empty = true;     flag_hidden = true;@@ -6466,7 +6453,7 @@ void init(int argc, const char **argv)   else if (strncmp(program, "fgrep", len) == 0)   {     // the 'fgrep' command is equivalent to 'ugrep --grep -F -. --sort'-    flag_fixed_strings = true;+    flag_mode = Mode::FIXED;     flag_grep = true;     flag_empty = true;     flag_hidden = true;@@ -6476,7 +6463,7 @@ void init(int argc, const char **argv)   {     // the 'zgrep' command is equivalent to 'ugrep --decompress --grep -G -. --sort'     flag_decompress = true;-    flag_basic_regexp = true;+    flag_mode = Mode::BRE;     flag_grep = true;     flag_empty = true;     flag_hidden = true;@@ -6495,7 +6482,7 @@ void init(int argc, const char **argv)   {     // the 'zfgrep' command is equivalent to 'ugrep --decompress --grep -F -. --sort'     flag_decompress = true;-    flag_fixed_strings = true;+    flag_mode = Mode::FIXED;     flag_grep = true;     flag_empty = true;     flag_hidden = true;@@ -6540,12 +6527,10 @@ void init(int argc, const char **argv)   if (flag_zmax > 99)     usage("option --zmax argument exceeds upper limit"); -  // -P disables -F, -G and -Z (P>F>G>E override) and does not allow -N patterns-  if (flag_perl_regexp)+  // -P is incompatible with option -Z and with -N patterns+  if (flag_mode == Mode::PERL)   { #if defined(HAVE_PCRE2) || defined(HAVE_BOOST_REGEX)-    flag_fixed_strings = false;-    flag_basic_regexp = false;     if (flag_fuzzy > 0)       usage("options -P and -Z are not compatible");     for (const auto& arg : pattern_args)@@ -6560,10 +6545,6 @@ void init(int argc, const char **argv)   if (flag_only_matching || flag_ungroup)     flag_empty = false; -  // -F disables -G (P>F>G>E override)-  if (flag_fixed_strings)-    flag_basic_regexp = false;-   // -e, -N, --and, --andnot, --not   if (!pattern_args.empty())   {@@ -6574,7 +6555,7 @@ void init(int argc, const char **argv)       {         if (arg.first == CNF::PATTERN::AND || arg.first == CNF::PATTERN::NOT)           flag_bool = true;-        else if (arg.first == CNF::PATTERN::NEG && flag_fixed_strings)+        else if (arg.first == CNF::PATTERN::NEG && flag_mode == Mode::FIXED)           usage("option -F with -% or -Q does not support -N PATTERN");       } @@ -8425,8 +8406,8 @@ void ugrep()   if (!flag_file.empty())   {     // -F: make newline-separated lines in regex literal with \Q and \E-    bool fixed_strings = flag_fixed_strings;-    const char *bar = flag_basic_regexp ? "\\|" : "|";+    bool fixed_strings = flag_mode == Mode::FIXED;+    const char *bar = flag_mode == Mode::BRE ? "\\|" : "|";      // PATTERN or -e PATTERN: add an ending '|' (or BRE '\|') to the regex to concatenate sub-expressions     if (!regex.empty())@@ -8519,7 +8500,7 @@ void ugrep()     {       // pop unused ending '|' (or BRE '\|') from the |-concatenated regexes in the regex string       regex.pop_back();-      if (flag_basic_regexp)+      if (flag_mode == Mode::BRE)         regex.pop_back();     } @@ -8528,7 +8509,7 @@ void ugrep()   }    // patterns ^, $ and ^$ are special cases to optimize for search-  if (!flag_fixed_strings && Static::bcnf.singleton_or_undefined())+  if (flag_mode != Mode::FIXED && Static::bcnf.singleton_or_undefined())   {     if (regex == "^$")     {@@ -8592,7 +8573,7 @@ void ugrep()   }    // patterns .* and .+ are common easy special cases to optimize for search-  if (!flag_fixed_strings && !flag_dotall && Static::bcnf.singleton_or_undefined())+  if (flag_mode != Mode::FIXED && !flag_dotall && Static::bcnf.singleton_or_undefined())   {     if (regex == ".*")     {@@ -8760,7 +8741,7 @@ void ugrep()     convert_flags |= reflex::convert_flag::unicode;    // -G: convert basic regex (BRE) to extended regex (ERE)-  if (flag_basic_regexp)+  if (flag_mode == Mode::BRE)     convert_flags |= reflex::convert_flag::basic;    // set reflex::Pattern options to enable multiline mode@@ -8788,8 +8769,8 @@ void ugrep()   // reflex::Matcher options   std::string matcher_options; -  // grep compatibility mode: literally match closing ) like GNU grep when not paired with ( unless -P or -F-  if (flag_grep && !flag_perl_regexp && !flag_fixed_strings)+  // grep compatibility mode: literally match closing ) like GNU grep when not paired with ( unless -F or -P+  if (flag_grep && flag_mode != Mode::FIXED && flag_mode != Mode::PERL)     convert_flags |= reflex::convert_flag::closing;    // -Y or --empty: permit empty pattern matches@@ -8811,7 +8792,7 @@ void ugrep()   // --index: search is only possible with compatible options   if (flag_index != NULL)   {-    if (flag_perl_regexp)+    if (flag_mode == Mode::PERL)       usage("options --index and -P (--perl-regexp) are not compatible");     if (!flag_filter.empty())       usage("options --index and --filter are not compatible");@@ -8870,7 +8851,7 @@ void ugrep()       }     }   }-  else if (flag_perl_regexp)+  else if (flag_mode == Mode::PERL)   {     // -P: Perl matching with PCRE2 or Boost.Regex #if defined(HAVE_PCRE2)@@ -13533,7 +13514,7 @@ void Grep::find_text_preview(const char *filename, const char *findpart, size_t  #if !defined(HAVE_PCRE2) && defined(HAVE_BOOST_REGEX)   // buffer all input to work around Boost.Regex partial matching bug, but this may throw std::bad_alloc if the file is too large-  if (flag_perl_regexp)+  if (flag_mode == Mode::PERL)     matcher->buffer(); #endif @@ -15261,7 +15242,7 @@ void version() #endif   std::cout << "ugrep " UGREP_VERSION;   if (flag_grep)-    std::cout << " (" << (flag_basic_regexp ? "" : flag_fixed_strings ? "f" : "e") << "grep compat)";+    std::cout << " (" << (flag_mode == Mode::BRE ? "" : flag_mode == Mode::FIXED ? "f" : "e") << "grep compat)";   std::cout << " " PLATFORM << #if defined(HAVE_AVX512BW)     (reflex::have_HW_AVX512BW() ? " +avx512" : (reflex::have_HW_AVX2() ? " +avx2" : reflex::have_HW_SSE2() ?  " +sse2" : " (no sse2!)")) <<