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"