File indexing completed on 2024-04-14 14:32:17

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