diff --git a/include/library/string.hpp b/include/library/string.hpp index 2f9d1fe2..6b71badd 100644 --- a/include/library/string.hpp +++ b/include/library/string.hpp @@ -230,14 +230,33 @@ private: regex_results regex(const std::string& regex, const std::string& str, const char* ex = NULL) throw(std::bad_alloc, std::runtime_error); +enum regex_match_mode +{ + REGEX_MATCH_REGEX = 0, + REGEX_MATCH_LITERIAL = 1, + REGEX_MATCH_IWILDCARDS = 2, + REGEX_MATCH_IREGEX = 3, +}; + /** * Regexp a string and return match result. * * Parameter regex: The regexp to apply. * Parameter str: The string to apply the regexp to. + * Parameter mode: Match mode. + * 0 => Case-senstive regex (default). + * 1 => Case-insensitive regex. + * 2 => * Returns: True if matches, false if not. */ -bool regex_match(const std::string& regex, const std::string& str) throw(std::bad_alloc, std::runtime_error); +bool regex_match(const std::string& regex, const std::string& str, enum regex_match_mode mode = REGEX_MATCH_REGEX) + throw(std::bad_alloc, std::runtime_error); + +/** + * Try match a case-insensitive string fragment and return the result. + * + * \param pattern The pattern to match a + */ /** * Cast string to bool. diff --git a/src/library/string.cpp b/src/library/string.cpp index 78847978..ef592935 100644 --- a/src/library/string.cpp +++ b/src/library/string.cpp @@ -119,9 +119,78 @@ regex_results regex(const std::string& regexp, const std::string& str, const cha return regex_results(); } -bool regex_match(const std::string& regexp, const std::string& str) throw(std::bad_alloc, std::runtime_error) +bool regex_match(const std::string& regexp, const std::string& str, enum regex_match_mode mode) + throw(std::bad_alloc, std::runtime_error) { - return regex(regexp, str); + static threads::lock m; + static std::map> regexps; + static std::map , std::pair> transform_cache; + std::string _regexp; + bool icase = false; + std::ostringstream y; + { + //See if we have cached transform. + threads::alock h(m); + auto key = std::make_pair(mode, regexp); + if(transform_cache.count(key)) { + auto entry = transform_cache[key]; + _regexp = entry.first; + icase = entry.second; + goto transformed; + } + } + switch(mode) { + case REGEX_MATCH_REGEX: + icase = false; + _regexp = regexp; + break; + case REGEX_MATCH_IWILDCARDS: + case REGEX_MATCH_LITERIAL: + for(size_t i = 0; i < regexp.length(); i++) + if(regexp[i] == '?' && mode == REGEX_MATCH_IWILDCARDS) + y << "."; + else if(regexp[i] == '*' && mode == REGEX_MATCH_IWILDCARDS) + y << ".*"; + else if(regexp[i] >= 'A' && regexp[i] <= 'Z') + y << regexp[i]; + else if(regexp[i] >= 'a' && regexp[i] <= 'z') + y << regexp[i]; + else if(regexp[i] >= '0' && regexp[i] <= '9') + y << regexp[i]; + else if((unsigned char)regexp[i] > 127) //UTF-8. + y << regexp[i]; + else + y << "\\" << regexp[i]; + _regexp = ".*" + y.str() + ".*"; + icase = true; + break; + case REGEX_MATCH_IREGEX: + icase = true; + _regexp = ".*" + regexp + ".*"; + break; + } +transformed: + threads::alock h(m); + auto key = std::make_pair(mode, regexp); + if(!transform_cache.count(key)) + transform_cache[key] = std::make_pair(_regexp, icase); + + if(!regexps.count(regexp)) { + boost::regex* y = NULL; + auto flags = boost::regex::extended & ~boost::regex::collate; + flags |= boost::regex::nosubs; + if(icase) flags |= boost::regex::icase; + try { + y = new boost::regex(_regexp, flags); + regexps[_regexp] = y; + } catch(std::bad_alloc& e) { + delete y; + throw; + } catch(std::exception& e) { + throw std::runtime_error(e.what()); + } + } + return boost::regex_match(str.begin(), str.end(), *(regexps[_regexp])); } namespace diff --git a/src/platform/wxwidgets/tracelogger.cpp b/src/platform/wxwidgets/tracelogger.cpp index fd4af546..c84227f4 100644 --- a/src/platform/wxwidgets/tracelogger.cpp +++ b/src/platform/wxwidgets/tracelogger.cpp @@ -845,49 +845,15 @@ namespace static boost::regex regex; if(pattern == "") return false; - if(pattern[0] == 'F') { - //Substring find. - if(pattern != last_find) { - std::string tmp = pattern; - tmp = tmp.substr(1); - regex = boost::regex(tmp, boost::regex_constants::literal | - boost::regex_constants::icase); - last_find = pattern; - } - } - if(pattern[0] == 'W') { - //wildcard find. - if(pattern != last_find) { - std::ostringstream y; - for(size_t i = 1; i < pattern.length(); i++) - if(pattern[i] == '?') - y << "."; - else if(pattern[i] == '*') - y << ".*"; - else if(pattern[i] >= 'A' && pattern[i] <= 'Z') - y << pattern[i]; - else if(pattern[i] >= 'a' && pattern[i] <= 'z') - y << pattern[i]; - else if(pattern[i] >= '0' && pattern[i] <= '9') - y << pattern[i]; - else - y << "\\" << pattern[i]; - std::string tmp = y.str(); - regex = boost::regex(tmp, boost::regex_constants::extended); - last_find = pattern; - } - } - if(pattern[0] == 'R') { - //regexp find. - if(pattern != last_find) { - std::string tmp = pattern; - tmp = tmp.substr(1); - regex = boost::regex(tmp, boost::regex_constants::extended | - boost::regex_constants::icase); - last_find = pattern; - } - } - return regex_search(candidate, regex); + std::string tmp = pattern; + tmp = tmp.substr(1); + if(pattern[0] == 'F') + return regex_match(tmp, candidate, REGEX_MATCH_LITERIAL); + if(pattern[0] == 'W') + return regex_match(tmp, candidate, REGEX_MATCH_IWILDCARDS); + if(pattern[0] == 'R') + return regex_match(tmp, candidate, REGEX_MATCH_IREGEX); + return false; } void wxwin_tracelog::on_menu(wxCommandEvent& e) @@ -933,6 +899,15 @@ namespace find_active = false; return; } + //Do syntax check. + try { + find_match(tmp, ""); + } catch(...) { + find_active = false; + wxMessageBox(towxstring("Invalid search pattern"), _T("Invalid pattern"), + wxICON_EXCLAMATION | wxOK, this); + return; + } find_string = tmp; find_active = true; find_line = 0;