/*
* This file is part of Octopi, an open-source GUI for pacman.
* Copyright (C) 2013 Alexandre Albuquerque Arnt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*/

#include <iostream>
#include "pacmanexec.h"
#include "strconstants.h"
#include "unixcommand.h"
#include "wmhelper.h"

#include <QRegularExpression>

/*
 * This class decouples pacman commands executing and parser code from Octopi's interface
 */

/*
 * Let's create the needed unixcommand object that will ultimately execute Pacman commands
 */
PacmanExec::PacmanExec(QObject *parent) : QObject(parent)
{  
  m_unixCommand = new UnixCommand(parent);
  m_iLoveCandy = UnixCommand::isILoveCandyEnabled();
  m_debugMode = false;
  m_processWasCanceled = false;
  m_retrievingPackages = false;
  m_parsingAPackageChange = false;
  m_numberOfPackages = 0;
  m_packageCounter = 0;
  m_errorRetrievingFileCounter = 0;
  m_listOfDotPacnewFiles.clear();

  m_sharedMemory=nullptr;
  //UnixCommand::removeTemporaryFiles();

  QObject::connect(m_unixCommand, SIGNAL( started() ), this, SLOT( onStarted()));

  QObject::connect(m_unixCommand, SIGNAL( finished ( int, QProcess::ExitStatus )),
                   this, SLOT( onFinished(int, QProcess::ExitStatus)));

  QObject::connect(m_unixCommand, SIGNAL( readyReadStandardOutput()),
                   this, SLOT( onReadOutput()));

  QObject::connect(m_unixCommand, SIGNAL( readyReadStandardError() ),
                   this, SLOT( onReadOutputError()));  

  QObject::connect(m_unixCommand, SIGNAL(commandToExecInQTermWidget(QString)),
                   this, SIGNAL(commandToExecInQTermWidget(QString)));
}

/*
 * Let's remove UnixCommand temporary file...
 */
PacmanExec::~PacmanExec()
{
  //m_unixCommand->removeSharedMemFiles();
  //m_unixCommand->removeTemporaryFile();
}

/*
 * Turns DEBUG MODE on or off
 */
void PacmanExec::setDebugMode(bool value)
{
  m_debugMode = value;
}

/*
 * Searches for the presence of the db.lock file
 */
bool PacmanExec::isDatabaseLocked()
{
  QString lockFilePath(ctn_PACMAN_DATABASE_LOCK_FILE);
  QFile lockFile(lockFilePath);

  return (lockFile.exists());
}

/*
 * Cancels the running pacman process using "killall pacman" and removing database lock file
 *
 * Returns qt-sudo exit code
 */
int PacmanExec::cancelProcess()
{
  m_processWasCanceled = true;
  return (m_unixCommand->cancelProcess(m_sharedMemory));
}

/*
 * Searches the given output for a series of verbs that a Pacman transaction may produce
 */
bool PacmanExec::searchForKeyVerbs(QString output)
{
  return (output.contains(QRegularExpression(QStringLiteral("Arming "))) ||
          output.contains(QRegularExpression(QStringLiteral("checking "))) ||
          //output.contains(QRegularExpression("loading ")) ||
          output.contains(QRegularExpression(QStringLiteral("installing "))) ||
          output.contains(QRegularExpression(QStringLiteral("upgrading "))) ||
          output.contains(QRegularExpression(QStringLiteral("downgrading "))) ||
          //output.contains(QRegularExpression("resolving ")) ||
          //output.contains(QRegularExpression("looking ")) ||
          output.contains(QRegularExpression(QStringLiteral("removing "))));
}

/*
 * Breaks the output generated by QProcess so we can parse the strings
 * and give a better feedback to our users (including showing percentages)
 *
 * Returns true if the given output was split
 */
bool PacmanExec::splitOutputStrings(QString output)
{
  bool res = true;

  QString msg = output.trimmed();
  QStringList msgs = msg.split(QRegularExpression(QStringLiteral("\\n")), Qt::SkipEmptyParts);

  for (const QString& m: msgs)
  {
    QStringList m2 = m.split(QRegularExpression(QStringLiteral("\\(\\s{0,3}[0-9]{1,4}/[0-9]{1,4}\\) ")), Qt::SkipEmptyParts);

    if (m2.count() == 1)
    {
      //Let's try another test... if it doesn't work, we give up.
      QStringList maux = m.split(QRegularExpression(QStringLiteral("%")), Qt::SkipEmptyParts);
      if (maux.count() > 1)
      {
        for (QString aux: maux)
        {
          aux = aux.trimmed();
          if (!aux.isEmpty())
          {
            if (aux.at(aux.length()-1).isDigit())
            {
              aux += QLatin1String("%");
            }

            if (m_debugMode) std::cout << "_split - case1: " << aux.toLatin1().data() << std::endl;
            parsePacmanProcessOutput(aux);
          }
        }
      }
      else if (maux.count() == 1)
      {
        if (!m.isEmpty())
        {
          if (m_debugMode) std::cout << "_split - case2: " << m.toLatin1().data() << std::endl;
          parsePacmanProcessOutput(m);
        }
      }
    }
    else if (m2.count() > 1)
    {
      for (const QString& m3: m2)
      {
        if (!m3.isEmpty())
        {
          if (m_debugMode) std::cout << "_split - case3: " << m3.toLatin1().data() << std::endl;
          parsePacmanProcessOutput(m3);
        }
      }
    }
    else res = false;
  }

  return res;
}

/*
 * Processes the output of the 'pacman process' so we can update percentages and messages at real time
 */
void PacmanExec::parsePacmanProcessOutput(const QString &output)
{
  static QString msgCache;
  static bool storeMsgCache=true;
  m_parsingAPackageChange = false;

  if (m_commandExecuting == ectn_RUN_IN_TERMINAL ||
      m_commandExecuting == ectn_RUN_SYSTEM_UPGRADE_IN_TERMINAL)
  {
    return;
  }

  bool continueTesting = false;
  int a;
  QString perc;
  QString msg = output;
  QString progressRun;
  QString progressEnd;

  msg.remove(QRegularExpression(QStringLiteral(".+\\[Y/n\\].+")));

  //Let's remove color codes from strings...
  msg.remove(QStringLiteral("\033[0;1m"));
  msg.remove(QStringLiteral("\033[0m"));
  msg.remove(QStringLiteral("[1;33m"));
  msg.remove(QStringLiteral("[00;31m"));
  msg.remove(QStringLiteral("\033[1;34m"));
  msg.remove(QStringLiteral("\033[0;1m"));
  msg.remove(QStringLiteral("[1;1m"));
  msg.remove(QStringLiteral("[1;0m"));
  msg.remove(QStringLiteral("[1;m"));
  msg.remove(QStringLiteral("[1"));
  msg.remove(QStringLiteral("c"));
  msg.remove(QStringLiteral("C"));
  msg.remove(QStringLiteral(""));
  msg.remove(QStringLiteral("[m[0;37m"));
  msg.remove(QStringLiteral("o"));
  msg.remove(QStringLiteral("[m"));
  msg.remove(QStringLiteral(";31m"));
  msg.remove(QStringLiteral(";32m"));
  msg.remove(QStringLiteral(";37m"));
  msg.remove(QStringLiteral("8;5;243m"));
  msg.remove(QStringLiteral("[c"));
  msg.remove(QStringLiteral("[mo"));
  msg.remove(QStringLiteral("[32m"));
  msg.remove(QStringLiteral("[33m"));
  msg.remove(QStringLiteral("(B[m"));
  msg.remove(QStringLiteral("[?25l"));
  msg.remove(QStringLiteral("[?25h"));
  msg.remove(QStringLiteral("[2F"));
  msg.remove(QStringLiteral("E "));
  msg.remove(QStringLiteral("F "));
  msg.remove(QRegularExpression(QStringLiteral("^\\($")));

  if (storeMsgCache) msgCache+=msg;

  if (msg.indexOf(QLatin1String(":: Synchronizing package databases...")) != -1)
    m_commandExecuting = ectn_SYNC_DATABASE;
  else if (msg.indexOf(QLatin1String(":: Starting full system upgrade...")) != -1)
  {
    m_commandExecuting = ectn_SYSTEM_UPGRADE;
  }
  else if ((msg.indexOf(QLatin1String("resolving dependencies...")) != -1) && m_commandExecuting == ectn_SYSTEM_UPGRADE)
  {
    msg = QLatin1String("<br>") + msg;
  }

  if (msg.indexOf(QLatin1String(":: Retrieving packages...")) != -1)
  {
    m_retrievingPackages = true;
  }

  if (SettingsManager::getShowPackageNumbersOutput())
  {
    QRegularExpression re(QStringLiteral("Packages? \\(\\d+\\)"));
    QRegularExpressionMatch match = re.match(msg);
    if (match.hasMatch())
    {
      QString aux_packages = match.captured(0);
      aux_packages.remove(QRegularExpression(QStringLiteral("Packages? \\(")));
      aux_packages.remove(QStringLiteral(")"));
      m_numberOfPackages = aux_packages.toInt();

      if (m_numberOfPackages > 0) m_packageCounter = 1;
      if (m_debugMode) std::cout << "Number of packages: " << m_numberOfPackages << std::endl;
    }
  }

  if (msg.contains(QLatin1String("Total Download Size:")))
  {
    storeMsgCache=false;
    msgCache.clear();

    //If we ever find a "pacman" package being updated in GUI mode, let's stop this transaction: potential breakage!
    if (msgCache.contains(QRegularExpression(QStringLiteral(".+Packages? \\(\\d+\\).+pacman-[0-9].+Total Download Size:.+"))))
    {
      cancelProcess();
    }
  }
  else if (
      (msg.contains(QLatin1String(":: waiting for 1 process to finish repacking"))) ||
      (msg.contains(QLatin1String(":: download complete in")))) return;

  else if (msg.contains(QLatin1String("download complete: ")))
  {
    prepareTextToPrint(msg + QLatin1String("<br>"));
    return;
  }

  if (m_debugMode) std::cout << "_treat: " << msg.toLatin1().data() << std::endl;

  if (m_iLoveCandy)
  {
    progressRun = QStringLiteral("m]");
    progressEnd = QStringLiteral("100%");
  }
  else
  {
    progressRun = QStringLiteral("-]");
    progressEnd = QStringLiteral("#]");
  }

  //If it is a percentage, we are talking about curl output...
  if(msg.indexOf(progressEnd) != -1)
  {
    perc = QStringLiteral("100%");
    emit percentage(100);
    continueTesting = true;
  }

  if (msg.indexOf(progressRun) != -1 || continueTesting)
  {
    if (!continueTesting){
      //perc = msg.right(4).trimmed();
      a = msg.lastIndexOf(QLatin1String("%"));
      perc = msg.mid(a-3, 3).trimmed() + QLatin1String("%");
      if (m_debugMode) std::cout << "percentage is: " << perc.toLatin1().data() << std::endl;
    }

    continueTesting = false;

    int aux = msg.indexOf(QLatin1String("["));
    if (aux > 0 && !msg.at(aux-1).isSpace()) return;

    QString target;
    if (m_commandExecuting == ectn_INSTALL ||
        m_commandExecuting == ectn_SYSTEM_UPGRADE ||
        m_commandExecuting == ectn_SYNC_DATABASE ||
        m_commandExecuting == ectn_REMOVE ||
        m_commandExecuting == ectn_REMOVE_INSTALL)
    {
      int ini = msg.indexOf(QRegularExpression(QStringLiteral("\\(\\s{0,3}[0-9]{1,4}/[0-9]{1,4}\\) ")));
      if (ini == 0)
      {
        int rp = msg.indexOf(QLatin1String(")"));
        msg = msg.remove(0, rp+2);

        if (searchForKeyVerbs(msg))
        {
          int end = msg.indexOf(QLatin1String("["));
          msg = msg.remove(end, msg.size()-end).trimmed() + QLatin1Char(' ');
          prepareTextToPrint(msg);
        }
        else
        {
          if (m_debugMode) std::cout << "test1: " << target.toLatin1().data() << std::endl;
          int pos = msg.indexOf(QLatin1String(" "));
          if (pos >=0)
          {
            target = msg.left(pos);
            target = target.trimmed() + QLatin1Char(' ');

            if (target.at(target.length()-1) == QLatin1String("%"))
              target.remove(target.length()-1, 1);

            if (m_commandExecuting != ectn_SYNC_DATABASE &&
              (!target.contains(QLatin1String("-i686")) && !target.contains(QLatin1String("-x86_64")) && !target.contains(QLatin1String("-any")))) return; //WATCHOUT!

            if (m_debugMode) std::cout << "target: " << target.toLatin1().data() << std::endl;

            if(!target.isEmpty())
            {
              prepareTextToPrint(QLatin1String("<b><font color=\"#b4ab58\">") + target + QLatin1String("</font></b>"));
            }
          }
          else
          {
            prepareTextToPrint(QLatin1String("<b><font color=\"#b4ab58\">") + msg + QLatin1String("</font></b>"));
          }
        }
      }
      else if (ini == -1)
      {
        if (searchForKeyVerbs(msg))
        {
          if (m_debugMode) std::cout << "test2: " << msg.toLatin1().data() << std::endl;

          int end = msg.indexOf(QLatin1String("["));
          msg = msg.remove(end, msg.size()-end);
          msg = msg.trimmed() + QLatin1Char(' ');
          prepareTextToPrint(msg);
        }
        else
        {
          int pos = msg.indexOf(QLatin1String(" "));
          if (pos >=0)
          {
            target = msg.left(pos);
            target = target.trimmed() + QLatin1Char(' ');
            if (m_debugMode) std::cout << "target: " << target.toLatin1().data() << std::endl;

            if (m_commandExecuting != ectn_SYNC_DATABASE &&
              (!target.contains(QLatin1String("-i686")) && !target.contains(QLatin1String("-x86_64")) && !target.contains(QLatin1String("-any")))) return; //WATCHOUT!

            if(!target.isEmpty() && !target.startsWith(QLatin1Char('[')) && !m_textPrinted.contains(target))
            {
              if (target.indexOf(QRegularExpression(QStringLiteral("[a-z]+"))) != -1)
              {
                if(m_commandExecuting == ectn_SYNC_DATABASE && !target.contains(QLatin1String("/")))
                {
                  prepareTextToPrint(QLatin1String("<b><font color=\"#FF8040\">") +
                                      StrConstants::getSyncing() + QLatin1Char(' ') + target + QLatin1String("</font></b>"));
                }
              }
            }
          }
          else
          {
            prepareTextToPrint(QLatin1String("<b><font color=\"blue\">") + msg + QLatin1String("</font></b>"));
          }
        }
      }
    }

    //Here we print the transaction percentage updating
    if(!perc.isEmpty() && perc.indexOf(QLatin1String("%")) > 0)
    {
      int ipercentage = perc.left(perc.size()-1).toInt();
      emit percentage(ipercentage);
    }
  }
  //It's another error, so we have to output it
  else
  {
    //Let's supress some annoying string bugs...
    msg.remove(QRegularExpression(QStringLiteral("^Password:")));
    msg.remove(QRegularExpression(QStringLiteral("Don't need password!!")));
    msg.remove(QRegularExpression(QStringLiteral("\\(process.+")));
    msg.remove(QRegularExpression(QStringLiteral("QXcbConnection: XCB error:.+")));
    msg.remove(QRegularExpression(QStringLiteral("Using the fallback.+")));
    msg.remove(QRegularExpression(QStringLiteral("Gkr-Message:.+")));
    msg.remove(QRegularExpression(QStringLiteral("kdesu.+")));
    msg.remove(QRegularExpression(QStringLiteral("kbuildsycoca.+")));
    msg.remove(QRegularExpression(QStringLiteral("Connecting to deprecated signal.+")));
    msg.remove(QRegularExpression(QStringLiteral("QVariant.+")));
    msg.remove(QRegularExpression(QStringLiteral("gksu-run.+")));
    msg.remove(QRegularExpression(QStringLiteral("GConf Error:.+")));
    msg.remove(QRegularExpression(QStringLiteral(":: Do.*")));
    msg.remove(QRegularExpression(QStringLiteral("org\\.kde\\.")));
    msg.remove(QRegularExpression(QStringLiteral("QCommandLineParser")));
    msg.remove(QRegularExpression(QStringLiteral("QCoreApplication.+")));
    msg.remove(QRegularExpression(QStringLiteral("Detected locale.+")));
    msg.remove(QRegularExpression(QStringLiteral("Fontconfig warning.+")));
    msg.remove(QRegularExpression(QStringLiteral("reading configurations from.+")));
    msg.remove(QRegularExpression(QStringLiteral(".+annot load library.+")));
    msg.remove(QRegularExpression(QStringLiteral("libGL error.+")));
    msg.remove(QRegularExpression(QStringLiteral("qt5ct:.+")));
    msg.remove(QRegularExpression(QStringLiteral("(lxqt|octopi|qt)-sudo:.+")));
    msg.remove(QRegularExpression(QStringLiteral("qt.qpa.plugin:.+")));
    msg.remove(QRegularExpression(QStringLiteral("qt.qpa.xcb:.+")));
    msg.remove(QRegularExpression(QStringLiteral("qt.qpa.wayland.+")));
    msg.remove(QRegularExpression(QStringLiteral("qt.xkb.compose:.+")));
    msg.remove(QRegularExpression(QStringLiteral("\\[sudo\\] password for.+")));
    msg.remove(QRegularExpression(QStringLiteral("Icon theme \".+")));
    msg.remove(QRegularExpression(QStringLiteral("Gtk-Message:.+")));
    msg.remove(QRegularExpression(QStringLiteral("\\[K$")));
    msg.remove(QRegularExpression(QStringLiteral("Detected locale \"C\".+")));
    msg.remove(QRegularExpression(QStringLiteral("Qt depends on a UTF-8 locale.+")));
    msg.remove(QRegularExpression(QStringLiteral("libEGL warning.+")));
    msg.remove(QRegularExpression(QStringLiteral("MESA: error:.*")));
    msg.remove(QRegularExpression(QStringLiteral("^for more information\\.")));
    msg.remove(QRegularExpression(QStringLiteral("QApplication:.+")));
    msg.remove(QRegularExpression(QStringLiteral("Available styles:.+")));

    msg = msg.trimmed();
    msg.remove(QRegularExpression(QStringLiteral("Total")));

    msg.remove(QRegularExpression(QStringLiteral("\\[\\d"))); //when ParallelDownloads is enabled in pacman.conf

    if (m_debugMode) std::cout << "debug: " << msg.toLatin1().data() << std::endl;

    QString order;
    int ini = msg.indexOf(QRegularExpression(QStringLiteral("\\(\\s{0,3}[0-9]{1,4}/[0-9]{1,4}\\) ")));
    if (ini == 0)
    {
      int rp = msg.indexOf(QLatin1String(")"));
      order = msg.left(rp+2);
      msg = msg.remove(0, rp+2);
    }

    if (!msg.isEmpty())
    {
      if (m_textPrinted.contains(msg + QLatin1Char(' '))) return;

      if (msg.contains(QRegularExpression(QStringLiteral("removing")))) //&& !m_textPrinted.contains(msg + " "))
      {
        //Does this package exist or is it a proccessOutput buggy string???
        QString pkgName = msg.mid(9).trimmed();

        if (pkgName.indexOf(QLatin1String("...")) != -1 || UnixCommand::isPackageInstalled(pkgName))
        {
          m_parsingAPackageChange = true;
          prepareTextToPrint(QLatin1String("<b><font color=\"#E55451\">") + msg + QLatin1String("</font></b>")); //RED
        }
      }
      else
      {
        QString altMsg = msg;

        if (msg.contains(QLatin1String(":: Updating")) && m_commandExecuting == ectn_SYNC_DATABASE)
          prepareTextToPrint(QLatin1String("<br><b>") + StrConstants::getSyncDatabases() + QLatin1String(" (pkgfile -u)</b><br>"));

        else if (msg.contains(QLatin1String("download complete: ")))
          prepareTextToPrint(altMsg);

        else if (msg.indexOf(QLatin1String(":: Synchronizing package databases...")) == -1 &&
            msg.indexOf(QLatin1String(":: Starting full system upgrade...")) == -1)
        {
          if (m_debugMode) std::cout << "Print in black: " << msg.toLatin1().data() << std::endl;

          if (m_commandExecuting == ectn_SYNC_DATABASE &&
              msg.contains(QLatin1String("is up to date")) && msg != QLatin1String("is up to date"))
          {
            emit percentage(100);

            int blank = msg.indexOf(QLatin1String(" "));
            QString repo = msg.left(blank);

            if (repo.contains(QLatin1String("warning"), Qt::CaseInsensitive) ||
                repo.contains(QLatin1String("error"), Qt::CaseInsensitive) ||
                repo.contains(QLatin1String("gconf"), Qt::CaseInsensitive) ||
                repo.contains(QLatin1String("failed"), Qt::CaseInsensitive) ||
                repo.contains(QLatin1String("fontconfig"), Qt::CaseInsensitive) ||
                repo.contains(QLatin1String("reading"), Qt::CaseInsensitive)) return;

            altMsg = repo + QLatin1Char(' ') + StrConstants::getIsUpToDate();
            altMsg = Package::removeColorCodesFromStr(altMsg);
            prepareTextToPrint(altMsg); //BLACK
          }
          else if (m_commandExecuting == ectn_INSTALL || m_commandExecuting == ectn_REMOVE || m_commandExecuting == ectn_SYSTEM_UPGRADE)
          {
            if (!altMsg.contains(QRegularExpression(QStringLiteral("\\s"))) && !altMsg.contains(QStringLiteral("--"))
                && altMsg.contains(QStringLiteral("-")) && m_retrievingPackages)
            {
              prepareTextToPrint(QLatin1String("<b><font color=\"#b4ab58\">") +
                                altMsg + QLatin1String("</font></b>"));
            }
            else if (altMsg != QStringLiteral("E"))
            {
              altMsg = Package::removeColorCodesFromStr(altMsg);
              prepareTextToPrint(altMsg); //BLACK
            }
          }
        }
      }
    }
  }
}

/*
 * Returns true if pacman processing hits post-download phase
 */
bool PacmanExec::criticalPhaseInTransaction(const QString &str)
{
  return str.contains(QStringLiteral("checking keys in keyring")) ||
str.contains(QStringLiteral("checking package integrity")) ||
str.contains(QStringLiteral("checking for file conflicts")) ||
str.contains(QStringLiteral(":: Running pre-transaction hooks")) ||
str.contains(QStringLiteral(":: Processing package changes"));
}

/*
 * Prepares a string parsed from pacman output to be printed by the UI
 */
void PacmanExec::prepareTextToPrint(QString str, TreatString ts, TreatURLLinks tl)
{
  if (m_debugMode) std::cout << "_print (begin): " << str.toLatin1().data() << std::endl;

  if (ts == ectn_DONT_TREAT_STRING)
  {
    emit textToPrintExt(str);
    return;
  }

  if (str.size() <= 3) return;

  //If the string waiting to be printed is from curl status OR any other unwanted string...
  if (!str.contains(QRegularExpression(QStringLiteral("<font color"))))
  {
    if ((str.contains(QRegularExpression(QStringLiteral("\\(\\d"))) &&
         (!str.contains(QLatin1String("target"), Qt::CaseInsensitive)) &&
         (!str.contains(QLatin1String("package"), Qt::CaseInsensitive))) ||
        (str.contains(QRegularExpression(QStringLiteral("\\d\\)"))) &&
         (!str.contains(QLatin1String("target"), Qt::CaseInsensitive)) &&
         (!str.contains(QLatin1String("package"), Qt::CaseInsensitive))) ||

        str.indexOf(QLatin1String("Enter a selection"), Qt::CaseInsensitive) == 0 ||
        str.indexOf(QLatin1String("Proceed with"), Qt::CaseInsensitive) == 0 ||
        str.indexOf(QLatin1String("%")) != -1 ||
        str.indexOf(QLatin1String("---")) != -1)
    {
      return;
    }
  }
  else if (str.indexOf(QLatin1String("o o")) != -1) return;
  else if ((str.indexOf(QLatin1String("upgrading")) != -1 ||
           str.indexOf(QLatin1String("installing")) != -1 ||
           str.indexOf(QLatin1String("reinstalling")) != -1)
           && str.indexOf(QLatin1String("[")) != -1) return;

  if (str.indexOf(QLatin1String("%")) != -1) str.remove(QLatin1String("%"));

  //If the str waiting to being print has not yet been printed...
  if(m_textPrinted.contains(str) && (!str.startsWith(QLatin1String("==>")) && !str.startsWith(QLatin1String("->"))))
  {
    return;
  }

  QString newStr = str;

  //If the string has already been colored...
  if(newStr.contains(QRegularExpression(QStringLiteral("<font color"))))
  {
    newStr += QLatin1String("<br>");
  }
  //Otherwise, let's process the string to see if it needs to be colored
  else
  {
    if(newStr.contains(QLatin1String("removing ")) ||
       newStr.contains(QLatin1String("could not ")) ||
       newStr.contains(QLatin1String("error:"), Qt::CaseInsensitive) ||
       newStr.contains(QLatin1String("failed")) ||
       newStr.contains(QLatin1String("is not synced")) ||
       newStr.contains(QLatin1String("could not be found")) ||
       newStr.contains(StrConstants::getCommandFinishedWithErrors()))
    {
      newStr = newStr.trimmed();
      if (newStr.contains(QRegularExpression(QStringLiteral("removing \\S+$"))))
      {
        //Does this package exist or is it a proccessOutput buggy string???
        QString pkgName = newStr.mid(9).trimmed();

        if ((pkgName.indexOf(QLatin1String("...")) == -1) || UnixCommand::isPackageInstalled(pkgName))
        {
          m_parsingAPackageChange = true;
        }
      }

      if (newStr.contains(QLatin1String("failed retrieving file")))
      {
        m_errorRetrievingFileCounter++;
        if (m_errorRetrievingFileCounter > 50) return;
      }

      newStr = QLatin1String("<b><font color=\"#E55451\">") + newStr + QLatin1String("&nbsp;</font></b>"); //RED
    }
    else if (newStr.contains(QLatin1String("warning"), Qt::CaseInsensitive) || (newStr.contains(QLatin1String("downgrading"))))
    {
      newStr = QLatin1String("<b><font color=\"#FF8040\">") + newStr + QLatin1String("</font></b>"); //ORANGE
    }
    else if(newStr.contains(QLatin1String("checking ")) ||
            newStr.contains(QLatin1String("is synced")) ||
            newStr.contains(QLatin1String("-- reinstalling")) ||
            newStr.contains(QLatin1String("installing ")) ||
            newStr.contains(QLatin1String("upgrading "))) /*||
            newStr.contains("loading "))*/
    {
      newStr = newStr.trimmed();
      if (newStr.contains(QRegularExpression(QStringLiteral("installing \\S+$"))) || newStr.contains(QRegularExpression(QStringLiteral("upgrading \\S+$"))))
      {
        if (SettingsManager::getShowPackageNumbersOutput())
        {
          newStr.prepend(QStringLiteral("(%1/%2) ").arg(m_packageCounter).arg(m_numberOfPackages));

          if (m_packageCounter < m_numberOfPackages) m_packageCounter++;
        }
      }

      newStr = QLatin1String("<b><font color=\"#4BC413\">") + newStr + QLatin1String("</font></b>"); //GREEN
    }
    else if (!newStr.contains(QLatin1String("::")))
    {
      newStr += QLatin1String("<br>");
    }
  }//end of string coloring process

  if (newStr.contains(QLatin1String("Synchronizing databases... (pkgfile -u)")))
  {
    emit canStopTransaction(false);
  }
  else if (newStr.contains(QLatin1String("::")))
  {
    newStr = QLatin1String("<br><B>") + newStr + QLatin1String("</B><br><br>");

    if (newStr.contains(QStringLiteral(":: Retrieving packages"))) emit canStopTransaction(true);
    else if (criticalPhaseInTransaction(newStr)) emit canStopTransaction(false);

    if (SettingsManager::getShowPackageNumbersOutput() &&
        (newStr.contains(QLatin1String(":: Retrieving packages")) || (newStr.contains(QLatin1String(":: Processing package changes"))) ||
         (newStr.contains(QLatin1String(":: Running pre-transaction hooks")))))
        m_packageCounter = 1;
  }

  if (!newStr.contains(QRegularExpression(QStringLiteral("<br")))) //It was an else!
  {
    newStr += QLatin1String("<br>");
  }

  if (tl == ectn_TREAT_URL_LINK)
    newStr = Package::makeURLClickable(newStr);

  if (m_debugMode) std::cout << "_print (end): " << str.toLatin1().data() << std::endl;

  //Let's append this string in the list of already printed strings (before we treat package counter code)
  m_textPrinted.append(str);

  //Package counter code...
  if (SettingsManager::getShowPackageNumbersOutput() && m_commandExecuting != ectn_SYNC_DATABASE && newStr.contains(QLatin1String("#b4ab58")))
  {
    int c = newStr.indexOf(QLatin1String("#b4ab58\">")) + 9;
    newStr.insert(c, QStringLiteral("(%1/%2) ").arg(m_packageCounter).arg(m_numberOfPackages));
    if (m_packageCounter < m_numberOfPackages) m_packageCounter++;
  }

  if (SettingsManager::getShowPackageNumbersOutput() && m_parsingAPackageChange)
  {
    int c = newStr.indexOf(QLatin1String("#E55451\">")) + 9;
    newStr.insert(c, QStringLiteral("(%1/%2) ").arg(m_packageCounter).arg(m_numberOfPackages));
    if (m_packageCounter < m_numberOfPackages) m_packageCounter++;
  }
  //Package counter code...

  if (newStr.contains(QLatin1String("Foreign/AUR package notification")))
    newStr = QLatin1String("<br><b> Foreign/AUR packages:</b><br>");

  else if (newStr.contains(QLatin1String("Orphaned package notification...")))
    newStr = QLatin1String("<br><b> Orphaned packages:</b><br>");

  else if (newStr.contains(QLatin1String("Checking for .pacnew and .pacsave files...")))
    newStr = QLatin1String("<br><b> Checking for .pacnew and .pacsave files...</b><br>");

  else if (newStr.contains(QLatin1String(".pac* files found:")))
    newStr = QLatin1String("<b><font color=\"#FF8040\">.pac* files found:</font></b><br>");

  else if (newStr.contains(QLatin1String("Please check and merge")))
    newStr = QLatin1String("<b><font color=\"#FF8040\">Please check and merge</font></b><br><br>");

  //Let's insert in the ".pacnew" messages list if we found one!
  else if (newStr.contains(QRegularExpression(QStringLiteral("installed as \\S+.pacnew"))))
  {
    if (!m_listOfDotPacnewFiles.contains(newStr))
      m_listOfDotPacnewFiles.append(newStr);
  }

  emit textToPrintExt(newStr);
}

/*
 * Whenever QProcess starts the pacman command...
 */
void PacmanExec::onStarted()
{
  //First we output the name of action we are starting to execute!
  if (m_commandExecuting == ectn_CHECK_UPDATES)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getCheckingForUpdates() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_MIRROR_CHECK)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getSyncMirror() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_SYNC_DATABASE)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getSyncDatabases() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_SYSTEM_UPGRADE || m_commandExecuting == ectn_RUN_SYSTEM_UPGRADE_IN_TERMINAL)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getSystemUpgradeMsg() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_REMOVE)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getRemovingPackages() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_CHANGE_INSTALL_REASON)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getChangingInstallReason() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_INSTALL)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getInstallingPackages() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_REMOVE_INSTALL)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getRemovingAndInstallingPackages() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }
  else if (m_commandExecuting == ectn_RUN_IN_TERMINAL)
  {
    prepareTextToPrint(QLatin1String("<b>") + StrConstants::getRunningCommandInTerminal() + QLatin1String("</b><br><br>"), ectn_DONT_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);
  }

  QString output = m_unixCommand->readAllStandardOutput();
  output = output.trimmed();

  if (!output.isEmpty())
  {
    prepareTextToPrint(output);
  }

  emit started();
}

/*
 * Whenever QProcess' read output is retrieved...
 */
void PacmanExec::onReadOutput()
{
  if (m_commandExecuting == ectn_CHECK_UPDATES)
  {
    QString output = m_unixCommand->readAllStandardOutput();

    if (!output.isEmpty())
    {
      //checkupdates outputs outdated packages like this: "apr 1.6.5-1 -> 1.7.0-1"
      if (m_listOfOutatedPackages.count() == 0)
        m_listOfOutatedPackages = output.split(QStringLiteral("\n"), Qt::SkipEmptyParts);
      else
      {
        //checkupdates returned more than 1 time from the QProcess event, so we have to concatenate the list...
        QString lastPackage = m_listOfOutatedPackages.last();
        QStringList newList = output.split(QStringLiteral("\n"), Qt::SkipEmptyParts);
        QString firstPackage = newList.first();
        QStringList partsLast = lastPackage.split(QStringLiteral(" "), Qt::SkipEmptyParts);
        QStringList partsFirst = firstPackage.split(QStringLiteral(" "), Qt::SkipEmptyParts);

        if (partsLast.count()<4 || partsFirst.count()<4)
        {
          m_listOfOutatedPackages.removeLast();          
          newList.removeFirst();
          lastPackage += firstPackage;
          m_listOfOutatedPackages.append(lastPackage);
          m_listOfOutatedPackages.append(newList);
        }
        else
        {
          newList = output.split(QStringLiteral("\n"), Qt::SkipEmptyParts);
          m_listOfOutatedPackages.append(newList);
        }
      }
    }

    output.replace(QLatin1String("\\n"), QLatin1String("<br>"));
    int i = output.lastIndexOf(QLatin1String("<"));
    if (i != -1) output.remove(i, 4);
    prepareTextToPrint(output, ectn_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);

    emit readOutput();
    return;
  }
  else if (m_commandExecuting == ectn_MIRROR_CHECK)
  {
    QString output = m_unixCommand->readAllStandardOutput();

    output.remove(QStringLiteral("[01;33m"));
    output.remove(QStringLiteral("\033[01;37m"));
    output.remove(QStringLiteral("\033[00m"));
    output.remove(QStringLiteral("\033[00;32m"));
    output.remove(QStringLiteral("[00;31m"));
    output.replace(QLatin1String("["), QLatin1String("'"));
    output.replace(QLatin1String("]"), QLatin1String("'"));
    output.remove(QStringLiteral("\n"));

    if (output.contains(QLatin1String("Checking"), Qt::CaseInsensitive))
    {
      output += QLatin1String("<br>");
    }

    prepareTextToPrint(output, ectn_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);

    emit readOutput();
    return;
  }

  if (WMHelper::getSUCommand().contains(QLatin1String("kdesu")))
  {
    QString output = m_unixCommand->readAllStandardOutput();

    if (m_commandExecuting == ectn_SYNC_DATABASE &&
        output.contains(QLatin1String("Usage: /usr/bin/kdesu [options] command")))
    {
      emit readOutput();
      return;
    }

    output = output.remove(QStringLiteral("Fontconfig warning: \"/etc/fonts/conf.d/50-user.conf\", line 14:"));
    output = output.remove(QStringLiteral("reading configurations from ~/.fonts.conf is deprecated. please move it to /home/arnt/.config/fontconfig/fonts.conf manually"));

    if (!output.trimmed().isEmpty())
    {
      splitOutputStrings(output);
    }
  }
  else if (WMHelper::getSUCommand().contains(QLatin1String("gksu")))
  {
    QString output = m_unixCommand->readAllStandardOutput();
    output = output.trimmed();

    if(!output.isEmpty() &&
       output.indexOf(QLatin1String(":: Synchronizing package databases...")) == -1 &&
       output.indexOf(QLatin1String(":: Starting full system upgrade...")) == -1)
    {
      prepareTextToPrint(output);
    }
  }

  emit readOutput();
}

/*
 * Whenever QProcess' read error output is retrieved...
 */
void PacmanExec::onReadOutputError()
{
  if (m_commandExecuting == ectn_MIRROR_CHECK)
  {
    QString output = m_unixCommand->readAllStandardError();

    output.remove(QStringLiteral("[01;33m"));
    output.remove(QStringLiteral("\033[01;37m"));
    output.remove(QStringLiteral("\033[00m"));
    output.remove(QStringLiteral("\033[00;32m"));
    output.remove(QStringLiteral("[00;31m"));
    output.remove(QStringLiteral("\n"));

    if (output.contains(QLatin1String("Checking"), Qt::CaseInsensitive))
      output += QLatin1String("<br>");

    prepareTextToPrint(output, ectn_TREAT_STRING, ectn_DONT_TREAT_URL_LINK);

    emit readOutputError();
    return;
  }

  QString msg = m_unixCommand->readAllStandardError();
  msg = msg.remove(QStringLiteral("Fontconfig warning: \"/etc/fonts/conf.d/50-user.conf\", line 14:"));
  msg = msg.remove(QStringLiteral("reading configurations from ~/.fonts.conf is deprecated. please move it to /home/arnt/.config/fontconfig/fonts.conf manually"));

  if (!msg.trimmed().isEmpty())
  {
    splitOutputStrings(msg);
  }

  emit readOutputError();
}

/*
 * Whenever QProcess finishes the pacman command...
 */
void PacmanExec::onFinished(int exitCode, QProcess::ExitStatus es)
{
  m_retrievingPackages = false;

  if (m_commandExecuting == ectn_REMOVE_KCP_PKG)
  {
    if (UnixCommand::getLinuxDistro() == ectn_KAOS &&
        UnixCommand::hasTheExecutable(ctn_KCP_TOOL))

      UnixCommand::execCommandAsNormalUser(QStringLiteral("kcp"), QStringList() << QStringLiteral("-u"));
  }

  if (m_processWasCanceled && PacmanExec::isDatabaseLocked()) exitCode = -1;

  emit finished(exitCode, es);
}

// --------------------- DO METHODS ------------------------------------

/*
 * Uses checkupdates command to check for outdated packages in a temporary database
 */
void PacmanExec::doCheckUpdates()
{
  m_commandExecuting = ectn_CHECK_UPDATES;
  m_listOfOutatedPackages.clear();
  m_unixCommand->executeCommandAsNormalUser(ctn_CHECKUPDATES_BINARY, QStringList());
}

/*
 * Retrieves outdated packages create by checkupdates
 */
QStringList PacmanExec::getOutdatedPackages()
{
  return m_listOfOutatedPackages;
}

/*
 * Retrieves .pacnew file list if any
 */
QStringList PacmanExec::getDotPacnewFileList()
{
  return m_listOfDotPacnewFiles;
}

/*
 * Calls mirror-check to check mirrors and returns output to UI
 */
void PacmanExec::doMirrorCheck()
{
  m_commandExecuting = ectn_MIRROR_CHECK;
  m_unixCommand->executeCommandAsNormalUser(ctn_MIRROR_CHECK_APP, QStringList());
}

/*
 * Calls pacman to change the "Install Reason" flag of each pkg in the list.
 * If the Install Reason was as dependency, change to explicitly.
 * If the Install Reason was explicitly, change to as dependency.
 */
void PacmanExec::doChangeInstallReason(const QHash<QString, QString> &listOfPackages)
{
  QString command;
  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    command += QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1String("; ");
  }

  QHash<QString, QString>::const_iterator i = listOfPackages.constBegin();
  while(i != listOfPackages.constEnd())
  {
    if (i.value().contains(StrConstants::getExplicitly(), Qt::CaseInsensitive))
    {
      command += QLatin1String("pacman -D --asdeps ") + i.key() + QLatin1String("; ");
    }
    else if (i.value().contains(StrConstants::getAsDependency(), Qt::CaseInsensitive))
    {
      command += QLatin1String("pacman -D --asexplicit ") + i.key() + QLatin1String("; ");
    }

    ++i;
  }

  m_commandExecuting = ectn_CHANGE_INSTALL_REASON;
  m_unixCommand->executeCommandWithSharedMemHelper(command, m_sharedMemory);
}

/*
 * Calls pacman to install given packages and returns output to UI
 */
void PacmanExec::doInstall(const QString &listOfPackages, const QString &listOfDeps)
{
  QString command;

  if (isDatabaseLocked())
  {
    command += QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1String("; ");
  }

  command += QLatin1String("pacman -S --noconfirm ") + listOfPackages;
  if (!listOfDeps.isEmpty())
    command += QLatin1String("; pacman -S --noconfirm --asdeps ") + listOfDeps;

  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';'));
  }

  m_lastCommandList.append(QLatin1String("pacman -S ") + listOfPackages + QLatin1Char(';'));

  if (!listOfDeps.isEmpty())
    m_lastCommandList.append(QLatin1String("pacman -S --asdeps ") + listOfDeps + QLatin1Char(';'));

  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_INSTALL;
  m_unixCommand->executeCommandWithSharedMemHelper(command, m_sharedMemory);
}

/*
 * Calls pacman to install given packages inside a terminal
 */
void PacmanExec::doInstallInTerminal(const QString &listOfPackages, const QString &listOfPackageDeps)
{
  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';'));
  }

  m_lastCommandList.append(QLatin1String("pacman -S ") + listOfPackages);

  if (!listOfPackageDeps.isEmpty())
    m_lastCommandList.append(QStringLiteral("pacman -S --asdeps ") + listOfPackageDeps);

  m_lastCommandList.append(QStringLiteral("echo -e"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));
  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runOctopiHelperInTerminalWithSharedMem(m_lastCommandList, m_sharedMemory);
}

/*
 * Calls pacman to install given LOCAL packages and returns output to UI
 */
void PacmanExec::doInstallLocal(const QString &listOfPackages)
{
  QString command;
  QStringList packages;
  bool dontUseForce = UnixCommand::isPacmanFiveDotOneOrHigher();

  if (isDatabaseLocked())
  {
    command += QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1String("; ");
  }

  packages=listOfPackages.split(QStringLiteral(";"), Qt::SkipEmptyParts);

  for(const QString& p: std::as_const(packages))
  {
    if(p.trimmed().isEmpty()) continue;
    if (dontUseForce)
      command += QLatin1String("LANG=C.UTF-8 pacman -U --noconfirm \"") + p.trimmed() + QLatin1String("\";");
    else
      command += QLatin1String("LANG=C.UTF-8 pacman -U --force --noconfirm \"") + p.trimmed() + QLatin1String("\";");
  }

  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';'));
  }

  for(const QString& p: std::as_const(packages))
  {
    if(p.trimmed().isEmpty()) continue;
    if (dontUseForce)
      m_lastCommandList.append(QLatin1String("LANG=C.UTF-8 pacman -U \"") + p.trimmed() + QLatin1String("\";"));
    else
      m_lastCommandList.append(QLatin1String("LANG=C.UTF-8 pacman -U --force \"") + p.trimmed() + QLatin1String("\";"));
  }

  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p '") + StrConstants::getPressAnyKey() + QLatin1Char('\''));

  m_commandExecuting = ectn_INSTALL;
  m_unixCommand->executeCommand(command);
}

/*
 * Calls pacman to install given LOCAL packages inside a terminal
 */
void PacmanExec::doInstallLocalInTerminal(const QString &listOfPackages)
{
  bool dontUseForce = UnixCommand::isPacmanFiveDotOneOrHigher();
  QString command;
  QStringList packages;

  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';'));
    command+=QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';');
  }

  packages=listOfPackages.split(QStringLiteral(";"), Qt::SkipEmptyParts);

  for(const QString& p: std::as_const(packages))
  {
    if(p.trimmed().isEmpty()) continue;

    if (dontUseForce)
    {
      command+=QLatin1String("pacman -U \"") + p.trimmed() + QLatin1String("\";");
      m_lastCommandList.append(QLatin1String("pacman -U \"") + p.trimmed() + QLatin1Char('"'));
    }
    else
    {
      command+=QLatin1String("pacman -U --force \"") + p.trimmed() + QLatin1String("\";");
      m_lastCommandList.append(QLatin1String("pacman -U --force \"") + p.trimmed() + QLatin1Char('"'));
    }
  }

  m_lastCommandList.append(QStringLiteral("echo -e"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));
  command+=QLatin1String("echo '") + StrConstants::getPressAnyKey() + QLatin1Char('\'');

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  //std::cout << "COMMAND: " << command.toLatin1().data() << std::endl;
  m_unixCommand->runCommandInTerminalWithSudo(command);
}

/*
 * Calls pacman to remove given packages and returns output to UI
 */
void PacmanExec::doRemove(const QString &listOfPackages)
{
  QString command;

  if (isDatabaseLocked())
  {
    command += QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1String("; ");
  }

  command += QLatin1String("pacman -R --noconfirm ") + listOfPackages;

  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';'));
  }

  m_lastCommandList.append(QLatin1String("pacman -R ") + listOfPackages + QLatin1Char(';'));
  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_REMOVE;  
  m_unixCommand->executeCommandWithSharedMemHelper(command, m_sharedMemory);
}

/*
 * Calls pacman to remove given packages inside a terminal
 */
void PacmanExec::doRemoveInTerminal(const QString &listOfPackages)
{
  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE);
  }

  m_lastCommandList.append(QLatin1String("pacman -R ") + listOfPackages);
  m_lastCommandList.append(QStringLiteral("echo -e"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runOctopiHelperInTerminalWithSharedMem(m_lastCommandList, m_sharedMemory);
}

/*
 * Calls pacman to remove and install given packages and returns output to UI
 */
void PacmanExec::doRemoveAndInstall(const QString &listOfPackagestoRemove, const QString &listOfPackagestoInstall, const QString &listOfPackagestoInstallDeps)
{
  QString command;

  if (isDatabaseLocked())
  {
    command += QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1String("; ");
  }

  command += QLatin1String("pacman -R --noconfirm ") + listOfPackagestoRemove + QLatin1String("; pacman -S --noconfirm ") + listOfPackagestoInstall;

  if (!listOfPackagestoInstallDeps.isEmpty())
    command += QLatin1String("; pacman -S --noconfirm --asdeps ") + listOfPackagestoInstallDeps;

  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';'));
  }

  m_lastCommandList.append(QLatin1String("pacman -R ") + listOfPackagestoRemove + QLatin1Char(';'));
  m_lastCommandList.append(QLatin1String("pacman -S ") + listOfPackagestoInstall + QLatin1Char(';'));

  if (!listOfPackagestoInstallDeps.isEmpty())
    m_lastCommandList.append(QLatin1String("pacman -S --asdeps ") + listOfPackagestoInstallDeps);

  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_REMOVE_INSTALL;
  m_unixCommand->executeCommandWithSharedMemHelper(command, m_sharedMemory);
}

/*
 * Calls pacman to remove and install given packages inside a terminal
 */
void PacmanExec::doRemoveAndInstallInTerminal(const QString &listOfPackagestoRemove, const QString &listOfPackagestoInstall, const QString &listOfPackagesToInstallDeps)
{
  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE);
  }

  m_lastCommandList.append(QLatin1String("pacman -R ") + listOfPackagestoRemove);
  m_lastCommandList.append(QLatin1String("pacman -S ") + listOfPackagestoInstall);

  if (!listOfPackagesToInstallDeps.isEmpty())
    m_lastCommandList.append(QLatin1String("pacman -S --asdeps ") + listOfPackagesToInstallDeps);

  m_lastCommandList.append(QStringLiteral("echo -e"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runOctopiHelperInTerminalWithSharedMem(m_lastCommandList, m_sharedMemory);
}

/*
 * Calls pacman to upgrade the entire system and returns output to UI
 */
void PacmanExec::doSystemUpgrade()
{
  QString command;

  if (isDatabaseLocked())
  {
    command += QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1String("; ");
  }

  command += QLatin1String("pacman -Fy; ");
  command += QLatin1String("pacman -Syu --noconfirm");

  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE + QLatin1Char(';'));
  }

  m_lastCommandList.append(QStringLiteral("pacman -Fy;"));
  m_lastCommandList.append(QStringLiteral("pacman -Syu;"));
  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_SYSTEM_UPGRADE;
  m_unixCommand->executeCommandWithSharedMemHelper(command, m_sharedMemory);
}

/*
 * Calls pacman to upgrade the entire system inside a terminal
 */
void PacmanExec::doSystemUpgradeInTerminal(CommandExecuting additionalCommand)
{
  m_lastCommandList.clear();

  if (isDatabaseLocked())
  {
    m_lastCommandList.append(QLatin1String("rm ") + ctn_PACMAN_DATABASE_LOCK_FILE);
  }

  bool isGarudaLinux = UnixCommand::getLinuxDistro() == ectn_GARUDALINUX;
  // If it is Garuda, we call garuda-update, instead of ordinary pacman -Syu
  if (isGarudaLinux)
  {
    m_lastCommandList.append(QStringLiteral("garuda-update"));
  }
  else
  {
    if (additionalCommand == ectn_NONE)
      m_lastCommandList.append(QStringLiteral("pacman -Syu"));
    else if (additionalCommand == ectn_SYNC_DATABASE)
      m_lastCommandList.append(QStringLiteral("pacman -Syu"));
  }

  m_lastCommandList.append(QStringLiteral("echo -e"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_SYSTEM_UPGRADE_IN_TERMINAL;
  m_unixCommand->runOctopiHelperInTerminalWithSharedMem(m_lastCommandList, m_sharedMemory);
}

/*
 * Calls AUR tool to upgrade given packages inside a terminal
 */
void PacmanExec::doAURUpgrade(const QString &listOfPackages)
{
  m_lastCommandList.clear();

  if (Package::getForeignRepositoryToolName() == ctn_PACAUR_TOOL)
  {
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -Sa ") + listOfPackages + QLatin1Char(';'));
  }
  else if (Package::getForeignRepositoryToolName() == ctn_TRIZEN_TOOL)
  {
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -Sa ") + listOfPackages + QLatin1Char(';'));
  }
  else if (Package::getForeignRepositoryToolName() == ctn_PIKAUR_TOOL)
  {
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S --aur ") + listOfPackages + QLatin1Char(';'));
  }
  else if (Package::getForeignRepositoryToolName() == ctn_YAY_TOOL)
  {
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S ") + listOfPackages + QLatin1Char(';'));
  }
  else if (Package::getForeignRepositoryToolName() == ctn_PARU_TOOL)
  {
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S ") + listOfPackages + QLatin1Char(';'));
  }

  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runCommandInTerminalAsNormalUser(m_lastCommandList);
}

/*
 * Calls AUR tool to remove and install given packages inside a terminal
 */
void PacmanExec::doAURRemoveAndInstallInTerminal(const QString &listOfPackagestoRemove, const QString &listOfPackagestoInstall)
{
  m_lastCommandList.clear();

  m_lastCommandList.append(QLatin1String("sudo pacman -R ") + listOfPackagestoRemove + QLatin1Char(';'));

  if (UnixCommand::getLinuxDistro() == ectn_KAOS)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -i ") + listOfPackagestoInstall + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_PACAUR_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -Sa ") + listOfPackagestoInstall + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_TRIZEN_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -Sa ") + listOfPackagestoInstall + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_PIKAUR_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S --aur ") + listOfPackagestoInstall + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_YAY_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S --aur ") + listOfPackagestoInstall + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_PARU_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S ") + listOfPackagestoInstall + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_CHASER_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" install ") + listOfPackagestoInstall + QLatin1Char(';'));

  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runCommandInTerminalAsNormalUser(m_lastCommandList);
}

/*
 * Calls pacman-key to refresh packager gpg keys
 */
void PacmanExec::doRefreshPacmanKeys()
{
  m_lastCommandList.clear();
  m_lastCommandList.append(QStringLiteral("/usr/bin/pacman-key --refresh-keys;"));
  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runCommandInTerminalWithSudo(m_lastCommandList);
}

/*
 * Calls AUR tool to install given packages inside a terminal
 */
void PacmanExec::doAURInstall(const QString &listOfPackages)
{
  m_lastCommandList.clear();

  if (UnixCommand::getLinuxDistro() == ectn_KAOS)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -i ") + listOfPackages + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_PACAUR_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -Sa ") + listOfPackages + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_TRIZEN_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -Sa ") + listOfPackages + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_PIKAUR_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S --aur ") + listOfPackages + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_YAY_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S --aur ") + listOfPackages + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_PARU_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" -S ") + listOfPackages + QLatin1Char(';'));
  else if (Package::getForeignRepositoryToolName() == ctn_CHASER_TOOL)
    m_lastCommandList.append(Package::getForeignRepositoryToolNameParam() + QLatin1String(" install ") + listOfPackages + QLatin1Char(';'));

  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runCommandInTerminalAsNormalUser(m_lastCommandList);
}

/*
 * Uses temporary yay-bin tool to get yay-bin package
 */
void PacmanExec::doInstallYayUsingTempYay()
{
  m_lastCommandList.clear();
  QString octopiConfDir = QDir::homePath() + QDir::separator() + QLatin1String(".config/octopi");
  QString cmd = octopiConfDir + QDir::separator() + QLatin1String("yay --noconfirm --editmenu=false -S yay-bin;");
  m_lastCommandList.append(cmd);
  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runCommandInTerminalAsNormalUser(m_lastCommandList);
}

/*
 * Calls AUR tool to remove given packages inside a terminal
 */
void PacmanExec::doAURRemove(const QString &listOfPackages)
{
  m_lastCommandList.clear();
  m_lastCommandList.append(QLatin1String("pacman -R ") + listOfPackages + QLatin1Char(';'));
  m_lastCommandList.append(QStringLiteral("echo -e;"));
  m_lastCommandList.append(QLatin1String("read -n 1 -p \"") + StrConstants::getPressAnyKey() + QLatin1Char('"'));

  if (Package::getForeignRepositoryToolName() == ctn_KCP_TOOL)
    m_commandExecuting = ectn_REMOVE_KCP_PKG;
  else
    m_commandExecuting = ectn_RUN_IN_TERMINAL;

  m_unixCommand->runOctopiHelperInTerminalWithSharedMem(m_lastCommandList, m_sharedMemory);
}

/*
 * Runs latest command inside a terminal (probably due to some previous error)
 */
void PacmanExec::runLatestCommandInTerminal()
{
  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runOctopiHelperInTerminalWithSharedMem(m_lastCommandList, m_sharedMemory);
}

/*
 * Runs latest command inside a terminal using "octopi-helper -t"
 */
void PacmanExec::runLatestCommandWithOctopiHelper()
{
  m_commandExecuting = ectn_RUN_IN_TERMINAL;
  m_unixCommand->runOctopiHelperInTerminalWithSharedMem(m_lastCommandList, m_sharedMemory);
}
