// // RegexString.cc for pekwm // Copyright © 2003-2009 Claes Nästén // // This program is licensed under the GNU GPL. // See the LICENSE file for more information. // #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include #include #include "RegexString.hh" #include "Util.hh" using std::cerr; using std::endl; using std::list; using std::string; using std::wstring; using std::strtol; const char RegexString::SEPARATOR = '/'; //! @brief RegexString constructor. RegexString::RegexString(void) : _reg_ok(false), _reg_inverted(false), _ref_max(1) { } //! @brief RegexString constructor with default search RegexString::RegexString(const std::wstring &str, bool full) : _reg_ok(false), _reg_inverted(false), _ref_max(1) { parse_match(str, full); } //! @brief RegexString destructor. RegexString::~RegexString(void) { free_regex(); } //! @brief Simple ed s command lookalike. bool RegexString::ed_s(std::wstring &str) { if (! _reg_ok) { return false; } string mb_str(Util::to_mb_str(str)); const char *c_str = mb_str.c_str(); regmatch_t *matches = new regmatch_t[_ref_max]; // Match if (regexec(&_regex, mb_str.c_str(), _ref_max, matches, 0)) { delete [] matches; return false; } string result; uint ref, size; list::iterator it(_ref_list.begin()); for (; it != _ref_list.end(); ++it) { if (it->get_reference() >= 0) { ref = it->get_reference(); if (matches[ref].rm_so != -1) { size = matches[ref].rm_eo - matches[ref].rm_so; result.append(string(c_str + matches[ref].rm_so, size)); } } else { result.append(Util::to_mb_str(it->get_string())); } } // Replace area regexp matched. size = matches[0].rm_eo - matches[0].rm_so; mb_str.replace(matches[0].rm_so, size, result); str = Util::to_wide_str(mb_str); return true; } //! @brief Parses match part of regular expression. //! @param match Expression. //! @param full Full expression if true (including flags). Defaults to false. bool RegexString::parse_match(const std::wstring &match, bool full) { // Free resources if (_reg_ok) { free_regex(); } if (match.size()) { int flags = REG_EXTENDED; string expression; wstring expression_str; // Full regular expression syntax, parse out flags etc string::size_type pos; if (match[0] == SEPARATOR && (pos = match.find_last_of(SEPARATOR)) != string::npos) { // Main expression expression_str = match.substr(1, pos - 1); // Expression flags for (string::size_type i = pos + 1; i < match.size(); ++i) { switch (match[i]) { case 'i': flags |= REG_ICASE; break; case '!': _reg_inverted = true; break; default: cerr << "Invalid flag \"" << match[i] << "\" for regular expression." << endl; break; } } expression = Util::to_mb_str(expression_str); } else { if (full) { cerr << "Invalid format of regular expression, missing separator " << SEPARATOR << endl; } expression = Util::to_mb_str(match); } _reg_ok = ! regcomp(&_regex, expression.c_str(), flags); } else { _reg_ok = false; } return _reg_ok; } //! @brief Parses replace part of ed_s command. //! Expects input in the style of /replace/me/. / can be any character //! except \. References to sub expressions are made with \num. \0 Represents //! the part of the string that matched. bool RegexString::parse_replace(const std::wstring &replace) { _ref_max = 0; wstring part; wstring::size_type begin = 0, end = 0, last = 0; // Go through the string and split at \num points while ((end = replace.find_first_of('\\', begin)) != string::npos) { // Store string between references. if (end > last) { part = replace.substr(last, end - last); _ref_list.push_back(RegexString::Part(part)); } // Get reference number. for (begin = ++end; isdigit(replace[end]); end++) ; if (end > begin) { // Convert number and add item. part = replace.substr(begin, end - last); int ref = strtol(Util::to_mb_str(part).c_str(), 0, 10); if (ref >= 0) { _ref_list.push_back(RegexString::Part(L"", ref)); if (ref > _ref_max) { _ref_max = ref; } } } last = end; begin = last + 1; } if (begin < replace.size()) { part = replace.substr(begin, replace.size() - begin); _ref_list.push_back(RegexString::Part(part)); } _ref_max++; return true; } //! @brief Parses ed s style command. /from/to/ bool RegexString::parse_ed_s(const std::wstring &ed_s) { if (ed_s.size() < 3) { return false; } wchar_t c_delimeter = ed_s[0]; string::size_type middle, end; // Middle. for (middle = 1; middle < ed_s.size(); middle++) { if ((ed_s[middle] == c_delimeter) && (ed_s[middle - 1] != '\\')) { break; } } // End. for (end = middle + 1; end < ed_s.size(); end++) { if ((ed_s[end] == c_delimeter) && (ed_s[end - 1] != '\\')) { break; } } wstring match, replace; match = ed_s.substr(1, middle - 1); replace = ed_s.substr(middle + 1, end - middle - 1); parse_match(match); parse_replace(replace); return true; } //! @brief Matches RegexString against rhs, needs successfull parse_match. bool RegexString::operator==(const std::wstring &rhs) { if (! _reg_ok) { return false; } string mb_rhs(Util::to_mb_str(rhs)); bool match = ! regexec(&_regex, mb_rhs.c_str(), 0, 0, 0); return _reg_inverted ? ! match : match; } //! @brief Free resources used by RegexString. void RegexString::free_regex(void) { if (_reg_ok) { regfree(&_regex); _reg_ok = false; } _reg_inverted = false; }