File indexing completed on 2025-01-12 06:47:24
0001 // 0002 // C++ Implementation: calias 0003 // 0004 // Description: 0005 // 0006 /* 0007 Copyright 2002-2011 Tomas Mecir <kmuddy@kmuddy.com> 0008 0009 This program is free software; you can redistribute it and/or 0010 modify it under the terms of the GNU General Public License as 0011 published by the Free Software Foundation; either version 2 of 0012 the License, or (at your option) any later version. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include "cactionmanager.h" 0024 #include "calias.h" 0025 #include "caliaslist.h" 0026 #include "ccmdqueue.h" 0027 #include "cexpresolver.h" 0028 #include "cpattern.h" 0029 #include "cscripteval.h" 0030 0031 // this structure holds the alias settings cached for faster access 0032 struct cAlias::Private { 0033 bool includeprefixsuffix; 0034 bool sendoriginal; 0035 bool global; 0036 QString condition; 0037 arith_exp *exp; 0038 cPattern p; 0039 cExpResolver *resolver; 0040 }; 0041 0042 cAlias::cAlias (cList *list) : cListObject (list) 0043 { 0044 d = new Private; 0045 0046 // these defaults must match the default property values set in cAliasList 0047 0048 //default compare type is "begins with" 0049 d->p.setMatching (cPattern::begin); 0050 //do not send original command by default 0051 d->sendoriginal = false; 0052 //include prefix/suffix 0053 d->includeprefixsuffix = true; 0054 d->global = false; 0055 d->exp = nullptr; 0056 d->resolver = nullptr; 0057 } 0058 0059 cAlias::~cAlias () 0060 { 0061 delete d->exp; 0062 delete d->resolver; 0063 delete d; 0064 } 0065 0066 void cAlias::updateVisibleName () 0067 { 0068 QString pattern = strVal ("pattern"); 0069 if (pattern.isEmpty()) 0070 cListObject::updateVisibleName(); 0071 else 0072 setVisibleName (pattern); 0073 } 0074 0075 void cAlias::attribChanged (const QString &name) 0076 { 0077 if (name == "pattern") { 0078 d->p.setPattern (strVal ("pattern")); 0079 updateVisibleName (); 0080 return; 0081 } 0082 if (name == "matching") { 0083 int m = intVal ("matching"); 0084 cPattern::PatternType pt; 0085 switch (m) { 0086 case 0: pt = cPattern::exact; break; 0087 case 1: pt = cPattern::substring; break; 0088 case 2: pt = cPattern::begin; break; 0089 case 3: pt = cPattern::end; break; 0090 case 4: pt = cPattern::regexp; break; 0091 default: pt = cPattern::begin; 0092 } 0093 d->p.setMatching (pt); 0094 return; 0095 } 0096 if (name == "cs") { 0097 d->p.setCaseSensitive (boolVal ("cs")); 0098 return; 0099 } 0100 if (name == "prefix-suffix") { 0101 d->includeprefixsuffix = boolVal ("prefix-suffix"); 0102 return; 0103 } 0104 if (name == "orig") { 0105 d->sendoriginal = boolVal ("orig"); 0106 return; 0107 } 0108 if (name == "whole-words") { 0109 d->p.setWholeWords (boolVal ("whole-words")); 0110 return; 0111 } 0112 if (name == "global") { 0113 d->global = boolVal ("global"); 0114 return; 0115 } 0116 if (name == "condition") { 0117 d->condition = strVal ("condition"); 0118 // TODO: this is duplicated for every place with conditions 0119 // find out if we could create a common class for this 0120 delete d->exp; 0121 d->exp = nullptr; 0122 0123 // no expression ? nothing to do ! 0124 if (d->condition.trimmed().isEmpty()) return; 0125 0126 // parse the condition 0127 arith_exp *exp = new arith_exp; 0128 bool ok = exp->compile (d->condition); 0129 if (ok) 0130 d->exp = exp; 0131 else 0132 // cannot parse condition - no conditional matching ... 0133 delete exp; 0134 return; 0135 } 0136 } 0137 0138 cList::TraverseAction cAlias::traverse (int traversalType) 0139 { 0140 if (traversalType == ALIAS_MATCH) 0141 return doMatch (); 0142 return cList::Stop; // unknown action 0143 } 0144 0145 cList::TraverseAction cAlias::doMatch () 0146 { 0147 cAliasList *al = (cAliasList *) list(); 0148 // fetch the string from the alias list 0149 QString string = al->stringToMatch(); 0150 int mpos = 0; // reset the index where matching will start 0151 //match against alias 0152 bool everMatched = false; 0153 while (true) { 0154 if (!d->p.match (string, mpos)) break; // match the string 0155 0156 // matched 0157 bool cond = testCondition (); // match the condition 0158 0159 // also matched - execute this alias ! 0160 // but don't break the loop if the condition didn't match, as global matching 0161 // relies on that 0162 if (cond) { 0163 everMatched = true; 0164 executeAlias (); 0165 } 0166 0167 if (!d->global) break; // only continue if it's a global alias 0168 0169 // global - update matching position, or terminate if no more matching should occur 0170 // if last length is 0, we must advance by 1 to avoid an endless loop ... 0171 int shift = (d->p.getLastLength() == 0) ? 1 : d->p.getLastLength(); 0172 mpos = d->p.getLastPos() + shift; 0173 if (mpos >= string.length()) 0174 break; 0175 } 0176 0177 // TODO: better control of when to continue / stop 0178 return everMatched ? cList::Stop : cList::Continue; 0179 } 0180 0181 // TODO: this is duplicated for every place with conditions 0182 // find out if we could create a common class for this 0183 bool cAlias::testCondition () 0184 { 0185 // no condition -> always matches ... 0186 if (!d->exp) return true; 0187 0188 if (!d->resolver) d->resolver = new cExpResolver (list()->session()); 0189 0190 // set up pseudo-variable expansion 0191 cCmdQueue *queue = new cCmdQueue (list()->session()); 0192 d->resolver->setQueue (queue); 0193 queue->fillFromPattern (&d->p); 0194 cValue val = d->exp->evaluate (d->resolver); 0195 delete queue; 0196 d->resolver->setQueue(nullptr); 0197 0198 // test passes if the evaluator returns non-zero ... 0199 return (val.asInteger() != 0); 0200 } 0201 0202 void cAlias::executeAlias () 0203 { 0204 // execute the script, if any 0205 // this needs to be done before the actual alias expansion 0206 QString script = strVal ("script"); 0207 if (!script.isEmpty()) { 0208 cActionManager *am = cActionManager::self(); 0209 int sess = list()->session(); 0210 cScriptEval *eval = dynamic_cast<cScriptEval *>(am->object ("scripteval", sess)); 0211 if (eval) eval->eval (script, d->p.scriptVariables()); 0212 } 0213 0214 cAliasList *al = (cAliasList *) list(); 0215 al->setMatched (); 0216 if (d->sendoriginal) al->wantOriginalCommand (); 0217 0218 for (int i = 1; i <= strListCount ("newtext"); ++i) { 0219 QString cmd = strListValue ("newtext", i); 0220 d->p.expandPseudoVariables (cmd); 0221 if (d->includeprefixsuffix) 0222 al->addCommand (d->p.getPrefix() + cmd + d->p.getSuffix()); 0223 else 0224 al->addCommand (cmd); 0225 } 0226 } 0227 0228