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