File indexing completed on 2024-12-01 06:51:47
0001 /*************************************************************************** 0002 crunninglist.cpp - list of running scripts 0003 ------------------- 0004 begin : Ne dec 22 2002 0005 copyright : (C) 2002-2009 by Tomas Mecir 0006 email : kmuddy@kmuddy.com 0007 ***************************************************************************/ 0008 0009 /*************************************************************************** 0010 * * 0011 * This program is free software; you can redistribute it and/or modify * 0012 * it under the terms of the GNU General Public License as published by * 0013 * the Free Software Foundation; either version 2 of the License, or * 0014 * (at your option) any later version. * 0015 * * 0016 ***************************************************************************/ 0017 0018 #include "crunninglist.h" 0019 0020 #include "cansiparser.h" 0021 #include "coutput.h" 0022 #include "crunningscript.h" 0023 #include "ctextchunk.h" 0024 0025 #include <QAbstractTableModel> 0026 #include <QRegExp> 0027 #include <KLocalizedString> 0028 0029 #include <stdlib.h> 0030 0031 /** Model used in the Running Scripts dialog. */ 0032 class cRunningModel: public QAbstractTableModel { 0033 public: 0034 cRunningModel (cRunningList *l) : 0035 QAbstractTableModel (0), lst (l) 0036 { 0037 } 0038 virtual ~cRunningModel () {} 0039 0040 virtual int columnCount (const QModelIndex &parent = QModelIndex()) const 0041 { 0042 if (parent.isValid()) return 0; // because Qt docs say so 0043 return 1; // we have 1 column 0044 } 0045 0046 virtual int rowCount (const QModelIndex &parent = QModelIndex()) const 0047 { 0048 if (parent.isValid()) return 0; // because Qt docs say so 0049 return lst->scripts.size(); 0050 } 0051 0052 virtual QVariant headerData ( int section, Qt::Orientation /*orientation*/, 0053 int role = Qt::DisplayRole ) const 0054 { 0055 if (role != Qt::DisplayRole) return QVariant(); 0056 switch (section) { 0057 case 0: return i18n ("Name"); 0058 default: return QVariant(); 0059 } 0060 } 0061 0062 virtual QVariant data ( const QModelIndex &index, int role = Qt::DisplayRole) const 0063 { 0064 // display and user role only 0065 if ((role != Qt::DisplayRole) && (role != Qt::UserRole)) return QVariant(); 0066 0067 if (index.parent().isValid()) return QVariant(); 0068 int row = index.row(); 0069 int col = index.column(); 0070 if ((col != 0)) return QVariant(); 0071 if ((row < 0) || (row > (int) lst->scripts.size())) return QVariant(); 0072 0073 map<int, cRunningScript *>::iterator it; 0074 const cRunningScript *entry = 0; 0075 int cnt = -1; 0076 for (it = lst->scripts.begin(); it != lst->scripts.end(); ++it) { 0077 if (++cnt < row) continue; 0078 entry = it->second; 0079 } 0080 if (!entry) return QVariant(); 0081 0082 // user role - return script ID 0083 if (role == Qt::UserRole) return entry->getId(); 0084 0085 // we want the displayed text - so fetch it and return it 0086 switch (col) { 0087 case 0: return entry->name(); 0088 default: return QVariant(); 0089 }; 0090 } 0091 0092 void resetModel () { 0093 reset (); 0094 } 0095 0096 private: 0097 cRunningList *lst; 0098 }; 0099 0100 0101 cRunningList::cRunningList (int sess) : cActionBase ("runninglist", sess) 0102 { 0103 scripts.clear (); 0104 lastid = 0; 0105 waitCounter = 0; 0106 waitLock = false; 0107 textQueue.clear (); 0108 typeQueue.clear (); 0109 0110 cANSIParser *ansiparser = dynamic_cast<cANSIParser *>(object ("ansiparser")); 0111 m_currentFgColor=ansiparser->defaultTextColor(); 0112 m_currentBkColor=ansiparser->defaultBkColor(); 0113 0114 model = new cRunningModel (this); 0115 0116 //sending text to scripts... 0117 // react on command-sent, got-line, got-prompt 0118 0119 addEventHandler ("command-sent", 200, PT_STRING); 0120 addEventHandler ("got-line", 200, PT_STRING); 0121 addEventHandler ("got-prompt", 200, PT_STRING); 0122 } 0123 0124 cRunningList::~cRunningList() 0125 { 0126 removeEventHandler ("command-sent"); 0127 removeEventHandler ("got-line"); 0128 removeEventHandler ("got-prompt"); 0129 0130 killAll (); 0131 0132 delete model; 0133 model = 0; 0134 } 0135 0136 QAbstractItemModel *cRunningList::getModel () 0137 { 0138 return model; 0139 } 0140 0141 void cRunningList::eventStringHandler (QString event, int, QString &par1, 0142 const QString &) 0143 { 0144 if (event == "command-sent") 0145 sendCommand (par1); 0146 else if (event == "got-line") 0147 sendServerOutput (par1); 0148 else if (event == "got-prompt") 0149 sendPrompt (par1); 0150 } 0151 0152 void cRunningList::addScript (cRunningScript *script) 0153 { 0154 //connect the script! 0155 connect (script, SIGNAL (sendText (const QString &)), this, 0156 SLOT (sendText (const QString &))); 0157 connect (script, SIGNAL (displayText (const QString &)), this, 0158 SLOT (displayText (const QString &))); 0159 connect (script, SIGNAL (scriptFinished (cRunningScript *, int)), this, 0160 SLOT (scriptFinished (cRunningScript *, int))); 0161 connect (script, SIGNAL (scriptKilled (cRunningScript *)), this, 0162 SLOT (scriptKilled (cRunningScript *))); 0163 connect (script, SIGNAL (scriptFailed (cRunningScript *)), this, 0164 SLOT (scriptFailed (cRunningScript *))); 0165 connect (script, SIGNAL (textSent ()), this, SLOT (scriptTextSent ())); 0166 connect (script, SIGNAL (textAccepted ()), this, SLOT (scriptTextAccepted ())); 0167 0168 //set script ID 0169 //FIXME: things may break down if we run out of free IDs! 0170 // (id is an 32-bit int) 0171 script->setId (++lastid); 0172 0173 //and add it to my list 0174 scripts[lastid] = script; 0175 0176 //inform about the change 0177 model->resetModel(); 0178 } 0179 0180 void cRunningList::killAll () 0181 { 0182 map<int, cRunningScript *>::iterator it; 0183 for (it = scripts.begin(); it != scripts.end(); ++it) 0184 delete it->second; 0185 scripts.clear(); 0186 } 0187 0188 const QString cRunningList::name (int id) 0189 { 0190 cRunningScript *rs = getRunningScript (id); 0191 return rs->name(); 0192 } 0193 0194 void cRunningList::terminate (int id) 0195 { 0196 cRunningScript *rs = getRunningScript (id); 0197 rs->terminate(); 0198 } 0199 0200 void cRunningList::kill (int id) 0201 { 0202 cRunningScript *rs = getRunningScript (id); 0203 rs->kill(); 0204 } 0205 0206 bool cRunningList::requestLock (cRunningScript *script, const QString &varname) 0207 { 0208 if (locks.count (varname)) 0209 { 0210 //look who has the lock 0211 cRunningScript *rs = locks[varname]; 0212 //report success if the caller has the lock :) 0213 if (script == rs) 0214 return true; 0215 //report failure otherwise 0216 return false; 0217 } 0218 else 0219 { 0220 //success 0221 locks[varname] = script; 0222 return true; 0223 } 0224 } 0225 0226 void cRunningList::releaseLock (cRunningScript *script, const QString &varname) 0227 { 0228 if (locks.count (varname)) 0229 if (locks[varname] == script) 0230 locks.erase (varname); 0231 } 0232 0233 bool cRunningList::canModify (cRunningScript *script, const QString &varname) 0234 { 0235 if (locks.count (varname)) 0236 return (locks[varname] == script); 0237 return true; 0238 } 0239 0240 cRunningScript *cRunningList::getRunningScript (int id) 0241 { 0242 if (!scripts.count (id)) return 0; 0243 return scripts[id]; 0244 } 0245 0246 void cRunningList::removeScript (cRunningScript *script) 0247 { 0248 //release all locks held by this script 0249 //we have to do this in two stages, because deleting from a map 0250 //immediately would invalidate our iterator (yes we could have two 0251 //iterators, but why bother...) 0252 //stage 1: obtain a list of all such locks 0253 list<QString> ourLocks; 0254 map<QString, cRunningScript *>::iterator it; 0255 for (it = locks.begin(); it != locks.end(); ++it) 0256 if (it->second == script) 0257 ourLocks.push_back (it->first); 0258 //stage 2: release all the locks 0259 list<QString>::iterator it2; 0260 for (it2 = ourLocks.begin(); it2 != ourLocks.end(); ++it2) 0261 locks.erase (*it2); 0262 0263 //delete the script 0264 scripts.erase (script->getId()); 0265 delete script; 0266 } 0267 0268 void cRunningList::scriptFinished (cRunningScript *script, int returnValue) 0269 { 0270 //display return code, if it is non-zero 0271 if (returnValue) 0272 invokeEvent ("message", sess(), "Script " + script->name() + 0273 " has finished with return code " + QString::number (returnValue) + "."); 0274 removeScript (script); 0275 0276 model->resetModel(); 0277 } 0278 0279 void cRunningList::scriptKilled (cRunningScript *script) 0280 { 0281 invokeEvent ("message", sess(), "Script " + script->name() + " has been terminated!"); 0282 removeScript (script); 0283 0284 model->resetModel(); 0285 } 0286 0287 void cRunningList::scriptFailed (cRunningScript *script) 0288 { 0289 invokeEvent ("message", sess(), "Script " + script->name() + " could not be started!"); 0290 0291 removeScript (script); 0292 0293 model->resetModel(); 0294 } 0295 0296 void cRunningList::sendThisNow (const QString &text, int type, bool noFC) 0297 { 0298 map<int, cRunningScript *>::iterator it; 0299 0300 if (noFC) 0301 { 0302 for (it = scripts.begin(); it != scripts.end(); ++it) 0303 //only scripts with the same no-flow-control setting 0304 if (!it->second->flowControl()) 0305 it->second->sendCommandToScript (text, type); 0306 } // endif non flow-controlled script 0307 else 0308 { 0309 waitLock = true; // Lock scriptTextSent() from sending data to scripts 0310 0311 sendToFlowControlled(text,type); 0312 0313 QString q_text; 0314 int q_type; 0315 0316 while ((waitCounter == 0) && 0317 (!textQueue.empty()) ) 0318 { 0319 q_text = textQueue.front (); 0320 q_type = typeQueue.front (); 0321 textQueue.pop_front (); 0322 typeQueue.pop_front (); 0323 sendToFlowControlled(q_text, q_type); 0324 0325 } // endwhile last script in list didn't send command and things in textQueue 0326 0327 waitLock = false; // Allow scriptTextSent() to send data to scripts 0328 } // endelse flow-controlled script 0329 0330 } // cRunningList::sendThisNow 0331 0332 0333 0334 void cRunningList::sendToFlowControlled(const QString &text, int type) 0335 { 0336 map<int, cRunningScript *>::iterator it; 0337 0338 waitCounter = 0; 0339 0340 // Count number of flow-controlled scripts 0341 for (it = scripts.begin(); it != scripts.end(); ++it) 0342 if (it->second->flowControl()) 0343 waitCounter++; 0344 0345 // Send data to flow-controlled scripts 0346 for (it = scripts.begin(); it != scripts.end(); ++it) 0347 { 0348 if (it->second->flowControl()) 0349 { 0350 it->second->sendCommandToScript (text, type); 0351 0352 if (!it->second->actuallySentCommand()) 0353 { 0354 waitCounter--; 0355 } // endif no command actually sent to script 0356 0357 } // endif flow-controlled script 0358 0359 } // endfor more flow-controlled scripts to count 0360 0361 } // cRunningList::sendToFlowControlled 0362 0363 0364 void cRunningList::sendText (const QString &text) 0365 { 0366 invokeEvent ("command", sess(), text); 0367 } 0368 0369 QColor * cRunningList::getColor(QString s) 0370 { 0371 QRegExp r = QRegExp("(\\d+),(\\d+),(\\d+)"); 0372 r.indexIn(s); 0373 QStringList rgbList = r.capturedTexts(); 0374 int red, green, blue; 0375 if(rgbList.size()==4) 0376 { 0377 // parse RGB color 0378 rgbList.pop_front(); //drop complete match 0379 red = rgbList.first().toInt(); 0380 rgbList.pop_front(); 0381 green = rgbList.first().toInt(); 0382 rgbList.pop_front(); 0383 blue = rgbList.first().toInt(); 0384 if((red>=0)&&(red<256)) 0385 if((green>=0)&&(green<256)) 0386 if((blue>=0)&&(blue<256)) 0387 { 0388 QColor * pC = new QColor(red,green,blue); 0389 return pC; 0390 } 0391 } 0392 return NULL; 0393 } 0394 0395 void cRunningList::displayText (const QString &text) 0396 { 0397 cConsole *console = (dynamic_cast<cOutput *>(object ("output")))->console(); 0398 /* cANSIParser *ansiparser = dynamic_cast<cANSIParser *>(object ("ansiparser")); 0399 cTextChunk *chunk = cTextChunk::makeLine (text, 0400 ansiparser->defaultTextColor(), ansiparser->defaultBkColor(), console);*/ 0401 cTextChunk *chunk = new cTextChunk(console, text); 0402 chunkStart start = chunk->startAttr(); 0403 start.fg = m_currentFgColor; 0404 start.bg = m_currentBkColor; 0405 chunk->setStartAttr (start); 0406 0407 /* DISABLED - this should go to the /echo command - and likely should be done better 0408 getColor also belongs to this 0409 0410 bool colorChange = false; 0411 QColor nextBkColor, nextFgColor; 0412 for( int i=0, charBehindLastTag=0, startOfCurrentTag=0; i<text.length();i++) 0413 { 0414 if(text[i]=='<') 0415 { 0416 startOfCurrentTag=i; 0417 // minimal tag size = 9 <fg0,0,0> or <bg0,0,0> 0418 // maximal tag size = 15 <fg255,255,255> or <bg...> 0419 if((i+10)<=text.length()) 0420 { 0421 if(text.mid(i+1,2)=="fg") 0422 { 0423 int tagEnd = text.indexOf('>',i+4); 0424 if((tagEnd>-1)&&(tagEnd<i+15)) 0425 { 0426 QColor * pC = getColor(text.mid(i+3,tagEnd-(i+3))); 0427 if(pC!=NULL) 0428 { 0429 nextFgColor = *pC; 0430 i=tagEnd; //+1 from the for loop 0431 colorChange = true; 0432 } 0433 } 0434 } 0435 if(text.mid(i+1,2)=="bg") 0436 { 0437 int tagEnd = text.indexOf('>',i+4); 0438 if((tagEnd>-1)&&(tagEnd<i+15)) 0439 { 0440 QColor * pC = getColor(text.mid(i+3,tagEnd-(i+3))); 0441 if(pC!=NULL) 0442 { 0443 nextBkColor = *pC; 0444 i=tagEnd; //+1 from the for loop 0445 colorChange = true; 0446 } 0447 } 0448 } 0449 } 0450 } 0451 if((colorChange)||(i==text.length())) 0452 { 0453 cTextChunk * tmp = 0; 0454 colorChange = false; 0455 if((startOfCurrentTag-charBehindLastTag)>=0) 0456 { 0457 tmp = new cTextChunk(console,text.mid(charBehindLastTag,startOfCurrentTag-charBehindLastTag)); 0458 } 0459 if(i==text.length()) 0460 { 0461 if(!colorChange) 0462 { 0463 tmp = new cTextChunk(console,text.mid(charBehindLastTag,text.length()-1)); 0464 } 0465 } 0466 0467 chunkStart start = tmp->startAttr(); 0468 start.fg = m_currentFgColor; 0469 start.bg = m_currentBkColor; 0470 tmp->setStartAttr (start); 0471 chunk->append(tmp); 0472 charBehindLastTag = i+1; // with i=endOfCurrentTag 0473 m_currentFgColor=nextFgColor; 0474 m_currentBkColor=nextBkColor; 0475 } 0476 } 0477 */ 0478 0479 invokeEvent ("display-line", sess(), chunk); 0480 delete chunk; // consumers create a copy of the chunk, so we just delete this 0481 } 0482 0483 void cRunningList::sendCommand (const QString &text) 0484 { 0485 //manage no-flow-control scripts first 0486 sendThisNow (text + "\n", USERCOMMAND, true); 0487 0488 //if we're not waiting for anything 0489 if ((waitCounter == 0) && (textQueue.empty())) 0490 sendThisNow (text + "\n", USERCOMMAND); 0491 else 0492 { 0493 textQueue.push_back (text + "\n"); 0494 typeQueue.push_back (USERCOMMAND); 0495 } 0496 } 0497 0498 void cRunningList::sendPrompt (const QString &text) 0499 { 0500 //manage no-flow-control scripts first 0501 sendThisNow (text + "\n", PROMPT, true); 0502 0503 //if we're not waiting for anything 0504 if ((waitCounter == 0) && (textQueue.empty())) 0505 sendThisNow (text + "\n", PROMPT); 0506 else 0507 { 0508 textQueue.push_back (text + "\n"); 0509 typeQueue.push_back (PROMPT); 0510 } 0511 } 0512 0513 void cRunningList::sendServerOutput (const QString &text) 0514 { 0515 //manage no-flow-control scripts first 0516 sendThisNow (text + "\n", SERVEROUTPUT, true); 0517 0518 //if we're not waiting for anything 0519 if ((waitCounter == 0) && (textQueue.empty())) 0520 sendThisNow (text + "\n", SERVEROUTPUT); 0521 else 0522 { 0523 textQueue.push_back (text + "\n"); 0524 typeQueue.push_back (SERVEROUTPUT); 0525 } 0526 } 0527 0528 void cRunningList::scriptTextAccepted () 0529 { 0530 // waitCounter++; 0531 } 0532 0533 void cRunningList::scriptTextSent () 0534 { 0535 waitCounter--; 0536 0537 if (waitLock == true) 0538 { 0539 return; 0540 } // endif sending from textQueue locked off 0541 else if ((waitCounter == 0) && (!(textQueue.empty()))) 0542 { 0543 //we're no longer waiting - send next line, if any 0544 0545 QString text = textQueue.front (); 0546 int type = typeQueue.front (); 0547 textQueue.pop_front (); 0548 typeQueue.pop_front (); 0549 sendThisNow (text, type); 0550 0551 } // endelse allowed to send from textQueue 0552 0553 } // scriptTextSent 0554 0555 #include "moc_crunninglist.cpp"