mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
255 lines
6.3 KiB
C++
255 lines
6.3 KiB
C++
|
//
|
||
|
// RegexString.cc for pekwm
|
||
|
// Copyright © 2003-2009 Claes Nästén <me{@}pekdon{.}net>
|
||
|
//
|
||
|
// 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 <iostream>
|
||
|
#include <cstdlib>
|
||
|
|
||
|
#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<RegexString::Part>::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;
|
||
|
}
|