/*************************************************************************** * Copyright (C) 2008-2013 by Heiko Koehn - KoehnHeiko@googlemail.com * * Copyright (C) 2014 by Ahmed Charles - acharles@outlook.com * * Copyright (C) 2016 by Chris Leacy - cleacy1972@gmail.com * * Copyright (C) 2017 by Stephen Lyons - slysven@virginmedia.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "TTrigger.h" #include "Host.h" #include "TConsole.h" #include "TDebug.h" #include "TMatchState.h" #include "mudlet.h" #include "pre_guard.h" #include #include "post_guard.h" #include #include using namespace std; const QString nothing = ""; TTrigger::TTrigger( TTrigger * parent, Host * pHost ) : Tree( parent ) , mTriggerContainsPerlRegex( false ) , mPerlSlashGOption( false ) , mFilterTrigger( false ) , mSoundTrigger( false ) , mStayOpen( 0 ) , mColorTrigger( false ) , mColorTriggerFg( false ) , mColorTriggerBg( false ) , mKeepFiring( 0 ) , mpHost( pHost ) , exportItem(true) , mModuleMasterFolder(false) , mNeedsToBeCompiled(true) , mTriggerType(REGEX_SUBSTRING) , mIsLineTrigger(false) , mStartOfLineDelta(0) , mLineDelta(3) , mIsMultiline(false) , mConditionLineDelta(0) , mpLua(mpHost->getLuaInterpreter()) , mFgColor(QColor(Qt::red)) , mBgColor(QColor(Qt::yellow)) , mIsColorizerTrigger(false) , mModuleMember(false) , mColorTriggerFgAnsi() , mColorTriggerBgAnsi() { } TTrigger::TTrigger(const QString& name, QStringList regexList, QList regexProperyList, bool isMultiline, Host* pHost) : Tree(0) , mTriggerContainsPerlRegex( false ) , mPerlSlashGOption( false ) , mFilterTrigger( false ) , mSoundTrigger( false ) , mStayOpen( 0 ) , mColorTrigger( false ) , mColorTriggerFg( false ) , mColorTriggerBg( false ) , mKeepFiring( 0 ) , mpHost( pHost ) , mName( name ) , mRegexCodeList( regexList ) , exportItem(true) , mModuleMasterFolder(false) , mRegexCodePropertyList(regexProperyList) , mNeedsToBeCompiled(true) , mTriggerType(REGEX_SUBSTRING) , mIsLineTrigger(false) , mStartOfLineDelta(0) , mLineDelta(3) , mIsMultiline(isMultiline) , mConditionLineDelta(0) , mpLua(mpHost->getLuaInterpreter()) , mFgColor(QColor(Qt::red)) , mBgColor(QColor(Qt::yellow)) , mIsColorizerTrigger(false) , mModuleMember(false) , mColorTriggerFgAnsi() , mColorTriggerBgAnsi() { setRegexCodeList(regexList, regexProperyList); } TTrigger::~TTrigger() { if (!mpHost) { return; } mpHost->getTriggerUnit()->unregisterTrigger(this); } void TTrigger::setName(const QString& name) { if( ! isTemporary() ) { mpHost->getTriggerUnit()->mLookupTable.remove( mName, this ); } mName = name; mpHost->getTriggerUnit()->mLookupTable.insertMulti(name, this); } static void pcre_deleter(pcre* pointer) { pcre_free(pointer); } //FIXME: sperren, wenn code nicht compiliert werden kann *ODER* regex falsch bool TTrigger::setRegexCodeList(QStringList regexList, QList propertyList) { regexList.replaceInStrings("\n", ""); mRegexCodeList.clear(); mRegexMap.clear(); mRegexCodePropertyList.clear(); mLuaConditionMap.clear(); mColorPatternList.clear(); mTriggerContainsPerlRegex = false; if (propertyList.size() != regexList.size()) { //FIXME: ronny hat das irgendwie geschafft qDebug() << "[CRITICAL ERROR (plz report):] Trigger name=" << mName << " aborting reason: propertyList.size() != regexList.size()"; } if ((propertyList.size() < 1) && (!isFolder()) && (!mColorTrigger)) { setError("No patterns defined."); mOK_init = false; return false; } bool state = true; for (int i = 0; i < regexList.size(); i++) { if (regexList[i].size() < 1) { continue; } mRegexCodeList.append(regexList[i]); mRegexCodePropertyList.append(propertyList[i]); if (propertyList[i] == REGEX_PERL) { const char* error; const QByteArray& local8Bit = regexList[i].toLocal8Bit(); int erroffset; QSharedPointer re(pcre_compile(local8Bit.constData(), 0, &error, &erroffset, 0), pcre_deleter); if (!re) { if (mudlet::debugMode) { TDebug(QColor(Qt::white), QColor(Qt::red)) << "REGEX ERROR: failed to compile, reason:\n" << error << "\n" >> 0; TDebug(QColor(Qt::red), QColor(Qt::gray)) << R"(in: ")" << local8Bit.constData() << "\"\n" >> 0; } setError(QStringLiteral("%1") .arg(tr(R"(Error: in item %1, perl regex: "%2", it failed to compile, reason: "%3".)").arg(QString::number(i), local8Bit.constData(), error))); state = false; } else { if (mudlet::debugMode) { TDebug(QColor(Qt::white), QColor(Qt::darkGreen)) << "[OK]: REGEX_COMPILE OK\n" >> 0; } } mRegexMap[i] = re; mTriggerContainsPerlRegex = true; } if (propertyList.at(i) == REGEX_LUA_CODE) { std::string funcName; std::stringstream func; func << "trigger" << mID << "condition" << i; funcName = func.str(); QString code = QStringLiteral("function %1()\n%2\nend\n").arg(funcName.c_str(), regexList[i]); QString error; if (!mpLua->compile(code, error, QString::fromStdString(funcName))) { setError(QStringLiteral("%1") .arg(tr(R"(Error: in item %1, lua condition function "%2" failed to compile, reason:%3.)").arg(QString::number(i), regexList.at(i), error))); state = false; if (mudlet::debugMode) { TDebug(QColor(Qt::white), QColor(Qt::red)) << "LUA ERROR: failed to compile, reason:\n" << error << "\n" >> 0; TDebug(QColor(Qt::red), QColor(Qt::gray)) << R"(in lua condition function: ")" << regexList.at(i) << "\"\n" >> 0; } } else { mLuaConditionMap[i] = funcName; } } if (propertyList[i] == REGEX_COLOR_PATTERN) { QRegExp regex = QRegExp(R"(FG(\d+)BG(\d+))"); int _pos = regex.indexIn(regexList[i]); if (_pos == -1) { mColorPatternList.push_back(0); state = false; continue; } int ansiFg = regex.cap(1).toInt(); int ansiBg = regex.cap(2).toInt(); if (!setupColorTrigger(ansiFg, ansiBg)) { mColorPatternList.push_back(0); state = false; continue; } } else { mColorPatternList.push_back(0); } } if (!state) { mOK_init = false; } else { mOK_init = true; } return state; } bool TTrigger::match_perl(char* subject, const QString& toMatch, int regexNumber, int posOffset) { assert(mRegexMap.contains(regexNumber)); QSharedPointer re = mRegexMap[regexNumber]; if (!re) { if (mudlet::debugMode) { TDebug(QColor(Qt::white), QColor(Qt::red)) << "ERROR:" << 0; TDebug(QColor(Qt::darkRed), QColor(Qt::darkGray)) << " the regex of trigger " << mName << " does not compile. Please correct the expression. This trigger will never match until it is fixed.\n" >> 0; } return false; //regex compile error } int numberOfCaptureGroups = 0; unsigned char* name_table; int namecount; int name_entry_size; int subject_length = strlen(subject); int rc, i; std::list captureList; std::list posList; int ovector[300]; // 100 capture groups max (can be increase nbGroups=1/3 ovector rc = pcre_exec(re.data(), 0, subject, subject_length, 0, 0, ovector, 100); if (rc < 0) { return false; } if (rc > 0) { if (mudlet::debugMode) { TDebug(QColor(Qt::blue), QColor(Qt::black)) << "Trigger name=" << mName << "(" << mRegexCodeList.value(regexNumber) << ") matched.\n" >> 0; } } if (rc == 0) { qDebug() << "CRITICAL ERROR: SHOULD NOT HAPPEN->pcre_info() got wrong num of cap groups ovector only has room for %d captured substrings\n"; } for (i = 0; i < rc; i++) { char* substring_start = subject + ovector[2 * i]; int substring_length = ovector[2 * i + 1] - ovector[2 * i]; std::string match; if (substring_length < 1) { captureList.push_back(match); posList.push_back(-1); continue; } match.append(substring_start, substring_length); captureList.push_back(match); posList.push_back(ovector[2 * i] + posOffset); if (mudlet::debugMode) { TDebug(QColor(Qt::darkCyan), QColor(Qt::black)) << "capture group #" << (i + 1) << " = " >> 0; TDebug(QColor(Qt::darkMagenta), QColor(Qt::black)) << "<" << match.c_str() << ">\n" >> 0; } } pcre_fullinfo(re.data(), NULL, PCRE_INFO_NAMECOUNT, &namecount); if (namecount <= 0) { ; } else { unsigned char* tabptr; pcre_fullinfo(re.data(), NULL, PCRE_INFO_NAMETABLE, &name_table); pcre_fullinfo(re.data(), NULL, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size); tabptr = name_table; for (i = 0; i < namecount; i++) { tabptr += name_entry_size; } } //TODO: add named groups seperately later as Lua::namedGroups if (mIsColorizerTrigger || mFilterTrigger) { numberOfCaptureGroups = captureList.size(); } for (; mPerlSlashGOption;) { int options = 0; int start_offset = ovector[1]; if (ovector[0] == ovector[1]) { if (ovector[0] >= subject_length) { goto END; } options = PCRE_NOTEMPTY | PCRE_ANCHORED; } rc = pcre_exec(re.data(), NULL, subject, subject_length, start_offset, options, ovector, 30); if (rc == PCRE_ERROR_NOMATCH) { if (options == 0) { break; } ovector[1] = start_offset + 1; continue; } if (rc < 0) { goto END; } if (rc == 0) { qDebug() << "CRITICAL ERROR: SHOULD NOT HAPPEN->pcre_info() got wrong num of cap groups ovector only has room for %d captured substrings\n"; } for (i = 0; i < rc; i++) { char* substring_start = subject + ovector[2 * i]; int substring_length = ovector[2 * i + 1] - ovector[2 * i]; std::string match; if (substring_length < 1) { captureList.push_back(match); posList.push_back(-1); continue; } match.append(substring_start, substring_length); captureList.push_back(match); posList.push_back(ovector[2 * i] + posOffset); if (mudlet::debugMode) { TDebug(QColor(Qt::darkCyan), QColor(Qt::black)) << " capture group #" << (i + 1) << " = " >> 0; TDebug(QColor(Qt::darkMagenta), QColor(Qt::black)) << "<" << match.c_str() << ">\n" >> 0; } } } END : { if (mIsColorizerTrigger) { int r1 = mBgColor.red(); int g1 = mBgColor.green(); int b1 = mBgColor.blue(); int r2 = mFgColor.red(); int g2 = mFgColor.green(); int b2 = mFgColor.blue(); int total = captureList.size(); TConsole* pC = mpHost->mpConsole; pC->deselect(); auto its = captureList.begin(); auto iti = posList.begin(); for (int i = 1; iti != posList.end(); ++iti, ++its, i++) { int begin = *iti; std::string& s = *its; int length = s.size(); if (total > 1) { // skip complete match in Perl /g option type of triggers // to enable people to highlight capture groups if there are any // otherwise highlight complete expression match if (i % numberOfCaptureGroups != 1) { pC->selectSection(begin, length); pC->setBgColor(r1, g1, b1); pC->setFgColor(r2, g2, b2); } } else { pC->selectSection(begin, length); pC->setBgColor(r1, g1, b1); pC->setFgColor(r2, g2, b2); } } pC->reset(); } if (mIsMultiline) { updateMultistates(regexNumber, captureList, posList); return true; } else { TLuaInterpreter* pL = mpHost->getLuaInterpreter(); pL->setCaptureGroups(captureList, posList); execute(); pL->clearCaptureGroups(); if (mFilterTrigger) { if (captureList.size() > 1) { int total = captureList.size(); auto its = captureList.begin(); auto iti = posList.begin(); for (int i = 1; iti != posList.end(); ++iti, ++its, i++) { int begin = *iti; std::string& s = *its; if (total > 1) { // skip complete match in Perl /g option type of triggers // to enable people to highlight capture groups if there are any // otherwise highlight complete expression match if (i % numberOfCaptureGroups != 1) { filter(s, begin); } } else { filter(s, begin); } } } } return true; } } return true; } bool TTrigger::match_begin_of_line_substring(const QString& toMatch, const QString& regex, int regexNumber, int posOffset) { if (toMatch.startsWith(regex)) { std::list captureList; std::list posList; captureList.push_back(regex.toLatin1().data()); posList.push_back(0 + posOffset); if (mudlet::debugMode) { TDebug(QColor(Qt::darkCyan), QColor(Qt::black)) << "Trigger name=" << mName << "(" << mRegexCodeList.value(regexNumber) << ") matched.\n" >> 0; } if (mIsColorizerTrigger) { int r1 = mBgColor.red(); int g1 = mBgColor.green(); int b1 = mBgColor.blue(); int r2 = mFgColor.red(); int g2 = mFgColor.green(); int b2 = mFgColor.blue(); TConsole* pC = mpHost->mpConsole; auto its = captureList.begin(); for (auto iti = posList.begin(); iti != posList.end(); ++iti, ++its) { int begin = *iti; std::string& s = *its; int length = s.size(); pC->selectSection(begin, length); pC->setBgColor(r1, g1, b1); pC->setFgColor(r2, g2, b2); } pC->reset(); } if (mIsMultiline) { updateMultistates(regexNumber, captureList, posList); return true; } else { TLuaInterpreter* pL = mpHost->getLuaInterpreter(); pL->setCaptureGroups(captureList, posList); // call lua trigger function with number of matches and matches itselves as arguments execute(); pL->clearCaptureGroups(); if (mFilterTrigger) { if (captureList.size() > 0) { filter(captureList.front(), posList.front()); } } return true; } } return false; } inline void TTrigger::updateMultistates(int regexNumber, std::list& captureList, std::list& posList) { if (regexNumber == 0) { // wird automatisch auf #1 gesetzt auto pCondition = new TMatchState(mRegexCodeList.size(), mConditionLineDelta); mConditionMap[pCondition] = pCondition; pCondition->multiCaptureList.push_back(captureList); pCondition->multiCapturePosList.push_back(posList); if (mudlet::debugMode) { TDebug(QColor(Qt::darkYellow), QColor(Qt::black)) << "match state " << mConditionMap.size() << "/" << mConditionMap.size() << " condition #" << regexNumber << "=true (" << regexNumber << "/" << mRegexCodeList.size() << ") regex=" << mRegexCodeList[regexNumber] << "\n" >> 0; } } else { int k = 0; for (auto& matchStatePair : mConditionMap) { k++; if (matchStatePair.second->nextCondition() == regexNumber) { if (mudlet::debugMode) { TDebug(QColor(Qt::darkYellow), QColor(Qt::black)) << "match state " << k << "/" << mConditionMap.size() << " condition #" << regexNumber << "=true (" << regexNumber << "/" << mRegexCodeList.size() << ") regex=" << mRegexCodeList[regexNumber] << "\n" >> 0; } matchStatePair.second->conditionMatched(); matchStatePair.second->multiCaptureList.push_back(captureList); matchStatePair.second->multiCapturePosList.push_back(posList); } } } } inline void TTrigger::filter(std::string& capture, int& posOffset) { if (capture.size() < 1) { return; } char* filterSubject = (char*)malloc(capture.size() + 2048); if (filterSubject) { strcpy(filterSubject, capture.c_str()); } else { return; } QString text = capture.c_str(); for (auto& trigger : *mpMyChildrenList) { trigger->match(filterSubject, text, -1, posOffset); } free(filterSubject); } bool TTrigger::match_substring(const QString& toMatch, const QString& regex, int regexNumber, int posOffset) { int where = toMatch.indexOf(regex); if (where != -1) { std::list captureList; std::list posList; captureList.push_back(regex.toLatin1().data()); posList.push_back(where + posOffset); if (mPerlSlashGOption) { while ((where = toMatch.indexOf(regex, where + 1)) != -1) { captureList.push_back(regex.toLatin1().data()); posList.push_back(where + posOffset); } } if (mudlet::debugMode) { TDebug(QColor(Qt::cyan), QColor(Qt::black)) << "Trigger name=" << mName << "(" << mRegexCodeList.value(regexNumber) << ") matched.\n" >> 0; } if (mIsColorizerTrigger) { int r1 = mBgColor.red(); int g1 = mBgColor.green(); int b1 = mBgColor.blue(); int r2 = mFgColor.red(); int g2 = mFgColor.green(); int b2 = mFgColor.blue(); TConsole* pC = mpHost->mpConsole; pC->deselect(); auto its = captureList.begin(); for (auto iti = posList.begin(); iti != posList.end(); ++iti, ++its) { int begin = *iti; std::string& s = *its; int length = s.size(); pC->selectSection(begin, length); pC->setBgColor(r1, g1, b1); pC->setFgColor(r2, g2, b2); } pC->reset(); } if (mIsMultiline) { updateMultistates(regexNumber, captureList, posList); return true; } else { TLuaInterpreter* pL = mpHost->getLuaInterpreter(); pL->setCaptureGroups(captureList, posList); // call lua trigger function with number of matches and matches itselves as arguments execute(); pL->clearCaptureGroups(); if (mFilterTrigger) { if (captureList.size() > 0) { filter(captureList.front(), posList.front()); } } return true; } } return false; } bool TTrigger::match_color_pattern(int line, int regexNumber) { if (regexNumber >= mColorPatternList.size()) { return false; } if (line == -1) { return false; } bool canExecute = false; std::list captureList; std::list posList; if (line >= static_cast(mpHost->mpConsole->buffer.buffer.size())) { return false; } std::deque& bufferLine = mpHost->mpConsole->buffer.buffer[line]; QString& lineBuffer = mpHost->mpConsole->buffer.lineBuffer[line]; int pos = 0; int matchBegin = -1; bool matching = false; TColorTable* pCT = mColorPatternList[regexNumber]; if (!pCT) { return false; //no color pattern created } int mFgR = pCT->fgR; int mFgG = pCT->fgG; int mFgB = pCT->fgB; int mBgR = pCT->bgR; int mBgG = pCT->bgG; int mBgB = pCT->bgB; for (auto it = bufferLine.begin(); it != bufferLine.end(); it++, pos++) { if (((*it).fgR == mFgR) && ((*it).fgG == mFgG) && ((*it).fgB == mFgB) && ((*it).bgR == mBgR) && ((*it).bgG == mBgG) && ((*it).bgB == mBgB)) { if (matchBegin == -1) { matchBegin = pos; } matching = true; } else { matching = false; } if ((!matching) || (matching && (pos + 1 >= static_cast(bufferLine.size())))) { if (matchBegin > -1) { std::string got; if (matching) { got = lineBuffer.mid(matchBegin, pos - matchBegin + 1).toLatin1().data(); } else { got = lineBuffer.mid(matchBegin, pos - matchBegin).toLatin1().data(); } captureList.push_back(got); posList.push_back(matchBegin); matchBegin = -1; canExecute = true; matching = false; } } } if (canExecute) { if (mIsColorizerTrigger) { int r1 = mBgColor.red(); int g1 = mBgColor.green(); int b1 = mBgColor.blue(); int r2 = mFgColor.red(); int g2 = mFgColor.green(); int b2 = mFgColor.blue(); TConsole* pC = mpHost->mpConsole; pC->deselect(); auto its = captureList.begin(); for (auto iti = posList.begin(); iti != posList.end(); ++iti, ++its) { int begin = *iti; std::string& s = *its; cout << "CTgot<" << s << "> bis:" << s.size() << endl; int length = s.size(); pC->selectSection(begin, length); pC->setBgColor(r1, g1, b1); pC->setFgColor(r2, g2, b2); } pC->reset(); } if (mIsMultiline) { updateMultistates(regexNumber, captureList, posList); return true; } else { TLuaInterpreter* pL = mpHost->getLuaInterpreter(); pL->setCaptureGroups(captureList, posList); // call lua trigger function with number of matches and matches itselves as arguments execute(); pL->clearCaptureGroups(); if (mFilterTrigger) { if (captureList.size() > 0) { auto it1 = captureList.begin(); auto it2 = posList.begin(); for (; it1 != captureList.end(); it1++, it2++) { filter(*it1, *it2); } } } return true; } } return false; } bool TTrigger::match_line_spacer(int regexNumber) { if (mIsMultiline) { int k = 0; for (auto& matchStatePair : mConditionMap) { k++; if (matchStatePair.second->nextCondition() == regexNumber) { if (matchStatePair.second->lineSpacerMatch(mRegexCodeList.value(regexNumber).toInt())) { if (mudlet::debugMode) { TDebug(QColor(Qt::yellow), QColor(Qt::black)) << "Trigger name=" << mName << "(" << mRegexCodeList.value(regexNumber) << ") condition #" << regexNumber << "=true " >> 0; TDebug(QColor(Qt::darkYellow), QColor(Qt::black)) << "match state " << k << "/" << mConditionMap.size() << " condition #" << regexNumber << "=true (" << regexNumber + 1 << "/" << mRegexCodeList.size() << ") line spacer=" << mRegexCodeList.value(regexNumber) << "lines\n" >> 0; } matchStatePair.second->conditionMatched(); std::list captureList; std::list posList; matchStatePair.second->multiCaptureList.push_back(captureList); matchStatePair.second->multiCapturePosList.push_back(posList); } } } } return true; //line spacers don't make sense outside of AND triggers -> ignore them } bool TTrigger::match_lua_code(int regexNumber) { if (mLuaConditionMap.find(regexNumber) == mLuaConditionMap.end()) { return false; } if (mpLua->callConditionFunction(mLuaConditionMap[regexNumber], mName)) { if (mudlet::debugMode) { TDebug(QColor(Qt::yellow), QColor(Qt::black)) << "Trigger name=" << mName << "(" << mRegexCodeList.value(regexNumber) << ") matched.\n" >> 0; } if (mIsMultiline) { std::list captureList; std::list posList; updateMultistates(regexNumber, captureList, posList); return true; } execute(); return true; } return false; } bool TTrigger::match_exact_match(const QString& toMatch, const QString& line, int regexNumber, int posOffset) { QString text = toMatch; if (text.endsWith(QChar('\n'))) { text.chop(1); //TODO: speed optimization } if (text == line) { std::list captureList; std::list posList; captureList.push_back(line.toLatin1().data()); posList.push_back(0 + posOffset); if (mudlet::debugMode) { TDebug(QColor(Qt::yellow), QColor(Qt::black)) << "Trigger name=" << mName << "(" << mRegexCodeList.value(regexNumber) << ") matched.\n" >> 0; } if (mIsColorizerTrigger) { int r1 = mBgColor.red(); int g1 = mBgColor.green(); int b1 = mBgColor.blue(); int r2 = mFgColor.red(); int g2 = mFgColor.green(); int b2 = mFgColor.blue(); TConsole* pC = mpHost->mpConsole; auto its = captureList.begin(); for (auto iti = posList.begin(); iti != posList.end(); ++iti, ++its) { int begin = *iti; std::string& s = *its; int length = s.size(); pC->selectSection(begin, length); pC->setBgColor(r1, g1, b1); pC->setFgColor(r2, g2, b2); } pC->reset(); } if (mIsMultiline) { updateMultistates(regexNumber, captureList, posList); return true; } else { TLuaInterpreter* pL = mpHost->getLuaInterpreter(); pL->setCaptureGroups(captureList, posList); // call lua trigger function with number of matches and matches itselves as arguments execute(); pL->clearCaptureGroups(); if (mFilterTrigger) { if (captureList.size() > 0) { filter(captureList.front(), posList.front()); } } return true; } } return false; } bool TTrigger::match(char* subject, const QString& toMatch, int line, int posOffset) { bool ret = false; if (isActive()) { if (mIsLineTrigger) { if (--mStartOfLineDelta < 0) { execute(); if (--mLineDelta <= 0) { deactivate(); mpHost->getTriggerUnit()->markCleanup(this); } return true; } return false; } if (toMatch.size() < 1) { return false; } bool conditionMet = false; int highestCondition = 0; if (mIsMultiline) { for (auto& matchStatePair : mConditionMap) { matchStatePair.second->newLineArrived(); int next = matchStatePair.second->nextCondition(); if (next > highestCondition) { highestCondition = next; } } } int size = mRegexCodePropertyList.size(); for (int i = 0;; i++) { if (i >= size) { break; } ret = false; switch (mRegexCodePropertyList.value(i)) { case REGEX_SUBSTRING: ret = match_substring(toMatch, mRegexCodeList[i], i, posOffset); break; case REGEX_PERL: ret = match_perl(subject, toMatch, i, posOffset); break; case REGEX_BEGIN_OF_LINE_SUBSTRING: ret = match_begin_of_line_substring(toMatch, mRegexCodeList[i], i, posOffset); break; case REGEX_EXACT_MATCH: ret = match_exact_match(toMatch, mRegexCodeList[i], i, posOffset); break; case REGEX_LUA_CODE: ret = match_lua_code(i); break; case REGEX_LINE_SPACER: ret = match_line_spacer(i); break; case REGEX_COLOR_PATTERN: ret = match_color_pattern(line, i); break; } // policy: one match is enough to fire on OR-trigger, but in the case of // an AND-trigger all conditions have to be met in order to fire the trigger if (!mIsMultiline) { if (ret) { conditionMet = true; mKeepFiring = mStayOpen; break; } } else { if ((!ret) && (i >= highestCondition)) { break; } } } // in the case of multiline triggers: check our state if (mIsMultiline) { int k = 0; conditionMet = false; //invalidate conditionMet as it has no meaning for multiline triggers list removeList; for (auto& matchStatePair : mConditionMap) { k++; if (matchStatePair.second->isComplete()) { mKeepFiring = mStayOpen; if (mudlet::debugMode) { TDebug(QColor(Qt::yellow), QColor(Qt::darkMagenta)) << "multiline trigger name=" << mName << " *FIRES* all conditons are fullfilled. Executing script.\n" >> 0; } removeList.push_back(matchStatePair.first); conditionMet = true; TLuaInterpreter* pL = mpHost->getLuaInterpreter(); pL->setMultiCaptureGroups(matchStatePair.second->multiCaptureList, matchStatePair.second->multiCapturePosList); execute(); pL->clearCaptureGroups(); if (mFilterTrigger) { std::list> multiCaptureList; multiCaptureList = matchStatePair.second->multiCaptureList; if (multiCaptureList.size() > 0) { for (auto mit = multiCaptureList.begin(); mit != multiCaptureList.end(); mit++, k++) { int total = (*mit).size(); auto its = (*mit).begin(); for (int i = 1; its != (*mit).end(); ++its, i++) { std::string s = *its; int p = 0; if (total > 1) { if (i % total != 1) { filter(s, p); } } else { filter(s, p); } } } } } } if (!matchStatePair.second->newLine()) { removeList.push_back(matchStatePair.first); } } for (auto& matchState : removeList) { if (mConditionMap.find(matchState) != mConditionMap.end()) { delete mConditionMap[matchState]; if (mudlet::debugMode) { TDebug(QColor(Qt::darkBlue), QColor(Qt::black)) << "removing condition from conditon table.\n" >> 0; } mConditionMap.erase(matchState); } } } // definition trigger chain: a folder is part of a trigger chain if it has a regex defined // a trigger chain only lets data pass if the condition matches or in case of multiline all // all conditions are fullfilled // // a folder can also be a simple structural element in which case all data passes through // if at least one regex is defined a folder is considered a trigger chain otherwise a structural element if (!mFilterTrigger) { if (conditionMet || (mRegexCodeList.size() < 1)) { for (auto trigger : *mpMyChildrenList) { ret = trigger->match(subject, toMatch, line); if (ret) { conditionMet = true; } } } } if ((mKeepFiring > 0) && (!conditionMet)) { mKeepFiring--; if ((mKeepFiring == mStayOpen) || (mpMyChildrenList->size() == 0)) { execute(); } for (auto trigger : *mpMyChildrenList) { ret = trigger->match(subject, toMatch, line); if (ret) { conditionMet = true; } } return true; } return conditionMet; } return false; } // Die Musternummer wird ID im color-pattern lookup table TColorTable* TTrigger::createColorPattern(int ansiFg, int ansiBg) { /* Mudlet simplified ANSI color codes ----------------------------------- 0 default text color 1 light black 2 dark black 3 light red 4 dark red 5 light green 6 dark green 7 light yellow 8 dark yellow 9 light blue 10 dark blue 11 light magenta 12 dark magenta 13 light cyan 14 dark cyan 15 light white 16 dark white */ bool invalidColorCode = false; int fgColorR = 0; int fgColorG = 0; int fgColorB = 0; int bgColorR = 0; int bgColorG = 0; int bgColorB = 0; int tag = ansiFg; if (tag <= 16) { QColor c; switch (tag) { case 0: c = mpHost->mFgColor; break; case 1: c = mpHost->mLightBlack; break; case 2: c = mpHost->mBlack; break; case 3: c = mpHost->mLightRed; break; case 4: c = mpHost->mRed; break; case 5: c = mpHost->mLightGreen; break; case 6: c = mpHost->mGreen; break; case 7: c = mpHost->mLightYellow; break; case 8: c = mpHost->mYellow; break; case 9: c = mpHost->mLightBlue; break; case 10: c = mpHost->mBlue; break; case 11: c = mpHost->mLightMagenta; break; case 12: c = mpHost->mMagenta; break; case 13: c = mpHost->mLightCyan; break; case 14: c = mpHost->mCyan; break; case 15: c = mpHost->mLightWhite; break; case 16: c = mpHost->mWhite; break; } fgColorR = c.red(); fgColorG = c.green(); fgColorB = c.blue(); } else { if (tag < 232) { tag -= 16; // because color 1-15 behave like normal ANSI colors // 6x6 RGB color space int r = tag / 36; int g = (tag - (r * 36)) / 6; int b = (tag - (r * 36)) - (g * 6); fgColorR = r * 42; fgColorG = g * 42; fgColorB = b * 42; } else if (tag < 256) { // black + 23 tone grayscale from dark to light gray tag -= 232; fgColorR = tag * 10; fgColorG = tag * 10; fgColorB = tag * 10; } else { //return invalid color error invalidColorCode = true; } } tag = ansiBg; if (tag <= 16) { QColor c; switch (tag) { case 0: c = mpHost->mBgColor; break; case 1: c = mpHost->mLightBlack; break; case 2: c = mpHost->mBlack; break; case 3: c = mpHost->mLightRed; break; case 4: c = mpHost->mRed; break; case 5: c = mpHost->mLightGreen; break; case 6: c = mpHost->mGreen; break; case 7: c = mpHost->mLightYellow; break; case 8: c = mpHost->mYellow; break; case 9: c = mpHost->mLightBlue; break; case 10: c = mpHost->mBlue; break; case 11: c = mpHost->mLightMagenta; break; case 12: c = mpHost->mMagenta; break; case 13: c = mpHost->mLightCyan; break; case 14: c = mpHost->mCyan; break; case 15: c = mpHost->mLightWhite; break; case 16: c = mpHost->mWhite; break; } bgColorR = c.red(); bgColorG = c.green(); bgColorB = c.blue(); } else { if (tag < 232) { tag -= 16; // because color 1-15 behave like normal ANSI colors // 6x6 RGB color space int r = tag / 36; int g = (tag - (r * 36)) / 6; int b = (tag - (r * 36)) - (g * 6); bgColorR = r * 42; bgColorG = g * 42; bgColorB = b * 42; } else if (tag < 256) { // black + 23 tone grayscale from dark to light gray tag -= 232; bgColorR = tag * 10; bgColorG = tag * 10; bgColorB = tag * 10; } else { //return invalid color error invalidColorCode = true; } } if (invalidColorCode) { return 0; } auto pCT = new TColorTable; if (!pCT) { return 0; } pCT->ansiBg = ansiBg; pCT->ansiFg = ansiFg; pCT->bgB = bgColorB; pCT->bgG = bgColorG; pCT->bgR = bgColorR; pCT->fgB = fgColorB; pCT->fgG = fgColorG; pCT->fgR = fgColorR; return pCT; } bool TTrigger::setupColorTrigger(int ansiFg, int ansiBg) { TColorTable* pCT = createColorPattern(ansiFg, ansiBg); if (!pCT) { return false; } mColorPatternList.push_back(pCT); return true; } bool TTrigger::setupTmpColorTrigger(int ansiFg, int ansiBg) { TColorTable* pCT = createColorPattern(ansiFg, ansiBg); if (!pCT) { return false; } QString code; code = QString("FG%1BG%2").arg(QString::number(ansiFg), QString::number(ansiBg)); mRegexCodeList << code; mRegexCodePropertyList << REGEX_COLOR_PATTERN; mColorPatternList.push_back(pCT); return true; } bool TTrigger::isFilterChain() { if ((mRegexCodeList.size() > 0) && (hasChildren())) { return true; } else { return false; } } bool TTrigger::registerTrigger() { if (!mpHost) { return false; } return mpHost->getTriggerUnit()->registerTrigger(this); } void TTrigger::compileAll() { mNeedsToBeCompiled = true; if (!compileScript()) { if (mudlet::debugMode) { TDebug(QColor(Qt::white), QColor(Qt::red)) << "ERROR: Lua compile error. compiling script of Trigger:" << mName << "\n" >> 0; } mOK_code = false; } setRegexCodeList(mRegexCodeList, mRegexCodePropertyList); for (auto trigger : *mpMyChildrenList) { trigger->compileAll(); } } void TTrigger::compile() { if (mNeedsToBeCompiled) { if (!compileScript()) { if (mudlet::debugMode) { TDebug(QColor(Qt::white), QColor(Qt::red)) << "ERROR: Lua compile error. compiling script of Trigger:" << mName << "\n" >> 0; } mOK_code = false; } } for (auto trigger : *mpMyChildrenList) { trigger->compile(); } } bool TTrigger::setScript(const QString& script) { mScript = script; mNeedsToBeCompiled = true; mOK_code = compileScript(); return mOK_code; } bool TTrigger::compileScript() { mFuncName = QString("Trigger") + QString::number(mID); QString code = QString("function ") + mFuncName + QString("()\n") + mScript + QString("\nend\n"); QString error; if (mpLua->compile(code, error, QString("Trigger: ") + getName())) { mNeedsToBeCompiled = false; mOK_code = true; return true; } else { mOK_code = false; setError(error); return false; } } void TTrigger::execute() { if (mSoundTrigger) { /* eventually something should be added to the gui to change sound volumes. 100=full volume */ mudlet::self()->playSound(mSoundFile, 100); } if (mCommand.size() > 0) { mpHost->send(mCommand); } if (mNeedsToBeCompiled) { if (!compileScript()) { return; } } if (mIsMultiline) { mpLua->callMulti(mFuncName, mName); } else { mpLua->call(mFuncName, mName); } } void TTrigger::enableTrigger(const QString& name) { if (mName == name) { setIsActive(true); } for (auto trigger : *mpMyChildrenList) { trigger->enableTrigger(name); } } void TTrigger::disableTrigger(const QString& name) { if (mName == name) { setIsActive(false); } for (auto trigger : *mpMyChildrenList) { trigger->disableTrigger(name); } } TTrigger* TTrigger::killTrigger(const QString& name) { if (mName == name) { setIsActive(false); } for (auto trigger : *mpMyChildrenList) { trigger->killTrigger(name); } return 0; }