1 /*
2  * RegexUtils.cpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #include <core/RegexUtils.hpp>
17 
18 #include <vector>
19 #include <sstream>
20 
21 #include <boost/regex.hpp>
22 #include <boost/algorithm/string.hpp>
23 
24 #include <boost/iostreams/copy.hpp>
25 #include <boost/iostreams/concepts.hpp>
26 #include <boost/iostreams/filtering_stream.hpp>
27 
28 #include <core/StringUtils.hpp>
29 
30 namespace rstudio {
31 namespace core {
32 namespace regex_utils {
33 
wildcardPatternToRegex(const std::string & pattern)34 boost::regex wildcardPatternToRegex(const std::string& pattern)
35 {
36    // split into componenents
37    using namespace boost::algorithm;
38    std::vector<std::string> components;
39    split(components, pattern, is_any_of("*"), token_compress_on);
40 
41    // build and return regex
42    std::string regex;
43    for (std::size_t i=0; i<components.size(); i++)
44    {
45       if (i > 0)
46          regex.append(".*");
47       regex.append("\\Q");
48       regex.append(components.at(i));
49       regex.append("\\E");
50    }
51    return boost::regex(regex);
52 }
53 
regexIfWildcardPattern(const std::string & term)54 boost::regex regexIfWildcardPattern(const std::string& term)
55 {
56    // create wildcard pattern if the search has a '*'
57    bool hasWildcard = term.find('*') != std::string::npos;
58    boost::regex pattern;
59    if (hasWildcard)
60       pattern = regex_utils::wildcardPatternToRegex(term);
61    return pattern;
62 }
63 
textMatches(const std::string & text,const boost::regex & regex,bool prefixOnly,bool caseSensitive)64 bool textMatches(const std::string& text,
65                  const boost::regex& regex,
66                  bool prefixOnly,
67                  bool caseSensitive)
68 {
69    boost::smatch match;
70    boost::match_flag_type flags = boost::match_default;
71    if (prefixOnly)
72       flags |= boost::match_continuous;
73    return regex_search(caseSensitive ? text : string_utils::toLower(text),
74                        match,
75                        regex,
76                        flags);
77 }
78 
filterString(const std::string & input,const std::vector<boost::iostreams::regex_filter> & filters,std::string * pOutput)79 Error filterString(const std::string& input,
80                    const std::vector<boost::iostreams::regex_filter>& filters,
81                    std::string* pOutput)
82 {
83    try
84    {
85       // create input stream
86       std::istringstream inputStream(input);
87       inputStream.exceptions(std::istream::failbit | std::istream::badbit);
88 
89       // create filtered output stream
90       std::ostringstream outputStream;
91       outputStream.exceptions(std::istream::failbit | std::istream::badbit);
92       boost::iostreams::filtering_ostream filteredStream;
93       for (std::size_t i=0; i<filters.size(); i++)
94          filteredStream.push(filters[i]);
95       filteredStream.push(outputStream);
96 
97       boost::iostreams::copy(inputStream, filteredStream, 128);
98 
99       *pOutput = outputStream.str();
100 
101       return Success();
102    }
103    catch(const std::exception& e)
104    {
105       Error error = systemError(boost::system::errc::io_error, ERROR_LOCATION);
106       error.addProperty("what", e.what());
107       error.addProperty("input", input.substr(0, 50));
108       return error;
109    }
110 
111    // keep compiler happy
112    return Success();
113 }
114 
filterString(const std::string & input,const boost::iostreams::regex_filter & filter,std::string * pOutput)115 Error filterString(const std::string& input,
116                    const boost::iostreams::regex_filter& filter,
117                    std::string* pOutput)
118 {
119    std::vector<boost::iostreams::regex_filter> filters;
120    filters.push_back(filter);
121    return filterString(input, filters, pOutput);
122 }
123 
124 
125 } // namespace regex_utils
126 } // namespace core
127 } // namespace rstudio
128 
129 
130 
131