File indexing completed on 2025-01-12 06:47:25
0001 /*************************************************************************** 0002 ccmdprocessor.cpp - command processor 0003 This file is a part of KMuddy distribution. 0004 ------------------- 0005 begin : Pi Jul 5 2002 0006 copyright : (C) 2002 by Tomas Mecir 0007 email : kmuddy@kmuddy.com 0008 ***************************************************************************/ 0009 0010 /*************************************************************************** 0011 * * 0012 * This program is free software; you can redistribute it and/or modify * 0013 * it under the terms of the GNU General Public License as published by * 0014 * the Free Software Foundation; either version 2 of the License, or * 0015 * (at your option) any later version. * 0016 * * 0017 ***************************************************************************/ 0018 0019 #include "ccmdprocessor.h" 0020 0021 #include "cexpresolver.h" 0022 #include "cglobalsettings.h" 0023 #include "cmacromanager.h" 0024 #include "cvariablelist.h" 0025 0026 #include <KLocalizedString> 0027 0028 #include <map> 0029 0030 class cExpCache { 0031 public: 0032 cExpCache (); 0033 ~cExpCache (); 0034 arith_exp *expression (const QString &src); 0035 void addExpression (const QString &src, arith_exp *exp); 0036 void clear (); 0037 protected: 0038 std::map<QString, arith_exp *> cache; 0039 }; 0040 0041 0042 cExpCache::cExpCache () 0043 { 0044 } 0045 0046 cExpCache::~cExpCache () 0047 { 0048 clear (); 0049 } 0050 0051 arith_exp *cExpCache::expression (const QString &src) 0052 { 0053 if (cache.count (src)) 0054 return cache[src]; 0055 return nullptr; 0056 } 0057 0058 void cExpCache::addExpression (const QString &src, arith_exp *exp) 0059 { 0060 // clear the cache if it's grown way too big 0061 // it's unlikely that anyone will ever hit this limit, but just in case ... 0062 if (cache.size() > 10000) clear(); 0063 0064 if (cache.count (src)) delete cache[src]; 0065 cache[src] = exp; 0066 } 0067 0068 void cExpCache::clear () 0069 { 0070 std::map<QString, arith_exp *>::iterator it; 0071 for (it = cache.begin(); it != cache.end(); ++it) 0072 delete it->second; 0073 cache.clear (); 0074 } 0075 0076 0077 cCmdProcessor::cCmdProcessor (int sess) : cActionBase ("cmdprocessor", sess) 0078 { 0079 focusstr = ":"; 0080 resolver = new cExpResolver (sess); 0081 expcache = new cExpCache; 0082 addGlobalEventHandler ("global-settings-changed", 50, PT_NOTHING); 0083 } 0084 0085 cCmdProcessor::~cCmdProcessor() 0086 { 0087 removeGlobalEventHandler ("global-settings-changed"); 0088 delete resolver; 0089 delete expcache; 0090 } 0091 0092 void cCmdProcessor::eventNothingHandler (QString event, int /*session*/) 0093 { 0094 if (event == "global-settings-changed") { 0095 setFocusCommandString (cGlobalSettings::self()->getString ("str-focus")); 0096 } 0097 } 0098 0099 void cCmdProcessor::setFocusCommandString (QString str) 0100 { 0101 focusstr = str.trimmed(); 0102 } 0103 0104 void cCmdProcessor::processCommand (const QString &command, cCmdQueue *queue) 0105 { 0106 QString cmd = command; // we need to modify the command ... 0107 0108 // expand internal scripting and variables, then send the command ... 0109 // also process command focusing if needed 0110 // internal scripting must be expanded first, because it can contain variable names, and 0111 // expandVariables would therefore mess things up ... 0112 expandInternalScripting (cmd, queue); 0113 expandVariables (cmd, queue); 0114 0115 // focusing command? 0116 bool focused = false; 0117 int pos; 0118 if ((pos = isFocusCommand (cmd)) != -1) { 0119 focused = true; 0120 if (processFocusCommand (cmd, pos) == -1) 0121 focused = false; 0122 } 0123 0124 if (!focused) 0125 // send the command ! 0126 invokeEvent ("send-command", sess(), cmd); 0127 } 0128 0129 void cCmdProcessor::expandInternalScripting (QString &command, cCmdQueue *queue) 0130 { 0131 // require profile connection ... 0132 if (!settings()) return; 0133 0134 resolver->setQueue (queue); 0135 if (command.indexOf ('[') == -1) // no internal scripting in this command ... 0136 return; 0137 0138 QString res, script; 0139 bool changed = false; 0140 // walk through the command and expand [] sequences as scripting, putting the result into 0141 // the "res" string 0142 int len = command.length(); 0143 bool backslash = false, in = false, instring = false; 0144 for (int i = 0; i < len; ++i) { 0145 char ch = command[i].toLatin1(); 0146 if (backslash) { 0147 backslash = false; 0148 if (in) 0149 script += command[i]; 0150 else 0151 res += command[i]; 0152 continue; 0153 } 0154 if (ch == '\\') { 0155 backslash = true; 0156 if (in) 0157 script += command[i]; 0158 else 0159 res += command[i]; 0160 continue; 0161 } 0162 if (!in) { 0163 if (ch == '[') { 0164 in = true; 0165 instring = false; 0166 script = ""; 0167 continue; 0168 } 0169 // add the letter to the result 0170 res += command[i]; 0171 } 0172 else { 0173 if (instring) { 0174 script += command[i]; 0175 if (ch == '"') 0176 instring = false; 0177 } else { 0178 if (ch == ']') 0179 { 0180 // end of script - evaluate it, assuming it's not empty 0181 script = script.trimmed (); 0182 if (script.length() > 0) { 0183 bool ok = true; 0184 cValue val = eval (script, queue, ok); 0185 if (ok) { 0186 res += val.asString (); 0187 changed = true; 0188 } else { 0189 // invokeEvent ("message", sess(), i18n ("Error in the script. Sending as-is.")); 0190 res += QChar('[') + script + QChar(']'); 0191 } 0192 } else { 0193 res += QChar('[') + script + QChar(']'); 0194 } 0195 script = ""; 0196 in = false; 0197 } 0198 else 0199 script += command[i]; 0200 } 0201 } 0202 } 0203 0204 if (in) // unterminated script = no script 0205 res += QChar ('[') + script; 0206 0207 // if there was some change, update the command ! 0208 if (changed) 0209 command = res; 0210 } 0211 0212 cValue cCmdProcessor::eval (const QString &e, cCmdQueue *queue, bool &ok) 0213 { 0214 resolver->setQueue (queue); 0215 ok = true; 0216 0217 // end of script - evaluate it, assuming it's not empty 0218 QString script = e.trimmed (); 0219 if (script.length() == 0) 0220 return cValue::empty(); 0221 0222 // try to fetch expression from the cache 0223 arith_exp *exp = expcache->expression (script); 0224 // expression is not in cache - need to create, compile and add to the cache 0225 if (!exp) { 0226 exp = new arith_exp; 0227 ok = exp->compile (script); 0228 if (ok) 0229 expcache->addExpression (script, exp); 0230 else 0231 delete exp; // wrong expression - get rid of it 0232 } 0233 if (ok) 0234 return exp->evaluate (resolver); 0235 else 0236 return cValue::empty(); 0237 } 0238 0239 void cCmdProcessor::expandVariables (QString &command, cCmdQueue *queue) 0240 { 0241 cVariableList *vl = dynamic_cast<cVariableList *>(object ("variables")); 0242 if (vl) 0243 command = vl->expandVariables (command, true, queue); 0244 } 0245 0246 int cCmdProcessor::isFocusCommand (const QString &command) 0247 { 0248 int pos; 0249 if ((focusstr.length() > 0) && (command.startsWith(focusstr))) { 0250 QString cmd = command.trimmed(); //removes leading/trailing spaces 0251 if ((pos = cmd.indexOf(focusstr, focusstr.length())) == -1) 0252 return -1; //return false if a second focustr can't be found 0253 return pos; 0254 } 0255 return -1; //should not get to here 0256 } 0257 0258 int cCmdProcessor::processFocusCommand (const QString &text, int pos) 0259 { 0260 QString window, command; 0261 window = command = text; 0262 0263 window.remove(pos, window.length()); 0264 window.remove(0,focusstr.length()); //remove first focusstr 0265 0266 command.remove(0,pos); 0267 command.remove(0,focusstr.length()); //remove next 0268 0269 if ( (command.length() < 1) || (window == focusstr) || (window.length() < 1)) 0270 return -1; //if nothing was passed after 2nd focusstr, return 0271 0272 invokeEvent ("focus-change", 0, window, command); 0273 0274 return 0; 0275 } 0276 0277 void cCmdProcessor::processMacro (const QString &mname, const QString ¶ms, cCmdQueue *queue) 0278 { 0279 cMacroManager *macros = dynamic_cast<cMacroManager *>(object ("macros", 0)); 0280 if (!macros) return; 0281 0282 if (!settings()) // -> not a profile-based connection 0283 { 0284 //exclaim that macros are not available 0285 invokeEvent ("message", sess(), i18n ("Sorry, but macro calls are only available for profile connections.")); 0286 return; 0287 } 0288 0289 QString mnamel = mname.toLower (); 0290 QString pars = params; // needed to get rid of the "const" modifier 0291 0292 expandInternalScripting (pars, queue); 0293 // we don't expand variables - macros must do that themselves 0294 // TODO: maybe allow the macro to specify whether it needs unexpanded vars ? 0295 if (!macros->callMacro (mnamel, pars, sess(), queue)) 0296 invokeEvent ("message", sess(), i18n ("This macro does not exist.")); 0297 0298 } 0299