//////////////////////////////////////////////////////////////
//      $Id: slackInterface.cpp,v 1.39 2002/08/15 16:06:16 binner Exp $
//
// Author: Toivo Pedaste
//
#include "../config.h"

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <time.h>
#include <setjmp.h>

#include <qdir.h>
#include <qfileinfo.h>
#include <qregexp.h>

#include <kurl.h>
#include <kglobal.h>
#include <kiconloader.h>

#include "packageInfo.h"
#include "slackInterface.h"
#include "updateLoc.h"
#include "kpackage.h"
#include "managementWidget.h"
#include "utils.h"
#include "procbuf.h"
#include "options.h"
#include "cache.h"
#include <klocale.h>


#define DIR "/var/log/packages/"
#define FILELIST "FILE LIST:\n"

enum {INITIAL, INSTALLED, UNINSTALLED};

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
SLACK::SLACK():pkgInterface()
{
  head = "SLACK";
  name = i18n("Slackware");
  icon = "slack";

  pict = UserIcon(icon);
  updated_pict = UserIcon("supdated");
  new_pict = UserIcon("snew");

  packagePattern = "*.tgz *.tar.gz";
  typeID = "/slack";

  locatedialog = 0;

  queryMsg = i18n("Querying SLACK package list: ");
  procMsg = i18n("Kpackage: Waiting on SLACK");

  locatedialog = new Locations(i18n("Location of Slackware Package Archives"));
  locatedialog->pLocations(1, 1, this,  i18n("I"),
  "Slackware", "*.TXT *.txt *.tgz *.tar.gz",
  i18n("Location of a 'PACKAGES.TXT' File for Extended Information"));
  locatedialog->pLocations(4, 1, this,  i18n("P"),
  "Slackware", "*.tgz *.tar.gz",
  i18n("Location of 'PACKAGES.TXT' File for Slackware Distribution"),
  i18n("Location of Base Directory of Slackware Distribution"));
  locatedialog->dLocations(2, 6, this,  i18n("D"),
  "Slackware", "*.tgz *.tar.gz",
  i18n("Location of Directories Containing Slackware Packages"));

  connect(locatedialog,SIGNAL(returnVal(LcacheObj *)),
  	  this,SLOT(setAvail(LcacheObj *)));
  locatedialog->apply_slot();

  paramsInst.append(new param(i18n("Test (do not install)"),FALSE,FALSE,"-warn"));

  paramsUninst.append(new param(i18n("Test (do not uninstall)"),FALSE,FALSE,"-warn"));


  initTranslate();
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
SLACK::~SLACK()
{
}

//////////////////////////////////////////////////////////////////////////////
void SLACK::initTranslate()
{
  trl = new QDict<QString>(53);

  trl->insert("a",new QString(i18n("Base System")));
  trl->insert("ap",new QString(i18n("Linux Applications")));
  trl->insert("d",new QString(i18n("Program Development")));
  trl->insert("e",new QString(i18n("GNU EMacs")));
  trl->insert("f",new QString(i18n("FAQs")));
  trl->insert("k",new QString(i18n("Kernel Source")));
  trl->insert("n",new QString(i18n("Networking")));
  trl->insert("t",new QString(i18n("TeX Distribution")));
  trl->insert("tcl",new QString(i18n("TCL Script Language")));
  trl->insert("x",new QString(i18n("X Window System")));
  trl->insert("xap",new QString(i18n("X Applications")));
  trl->insert("xd",new QString(i18n("X Development Tools")));
  trl->insert("xv",new QString(i18n("XView and OpenLook")));
  trl->insert("y",new QString(i18n("Games")));
}

// check if slack file
bool SLACK::isType(char *buf, const QString &)
{
  if ((unsigned char)buf[0] == 037 && (unsigned char)buf[1] == 0213 ) {
    return true;
  } else
    return false;
}

bool SLACK::parseName(const QString &name, QString *n, QString *v)
{
  int  s1;
  s1 = name.findRev('.');
  if (s1 > 0) {
    *n = name.left(s1);
    v = new QString("");
    return TRUE;
  }
  return FALSE;
}

void SLACK::listPackages(QPtrList<packageInfo> *pki)
{
  QString s;
  cacheObj *cp;

  if (packageLoc) {
    for (cp = packageLoc->first(); cp != 0; cp = packageLoc->next()) {
      // first entry is special
      if (cp->cacheFile == "SLACK_0_0") {
	s = getPackList(cp);
	if (!s.isEmpty()) {
	  listPackList(pki, s, cp, INITIAL);
	}
      }
    }
  }

  listInstalledPackages(pki);

  if (packageLoc) {
    for (cp = packageLoc->first(); cp != 0; cp = packageLoc->next()) {
      if (cp->cacheFile == "SLACK_0_0") {
	// already done
      } else if (cp->base != "") {
	s = getPackList(cp);
	if (!s.isEmpty()) {
	  listPackList(pki, s, cp, UNINSTALLED);
	}
      } else {
	s = getDir(cp);
	if (!s.isEmpty()) {
	  listDir(pki,s,cp->location);
	}
      }
    }
  }
}

void SLACK::listInstalledPackages(QPtrList<packageInfo> *pki)
{
  FILE *file;
  char linebuf[1024];
  QString vb;
  packageInfo *p;
  QString fn, dr = DIR;

  QDir d(DIR);
  if (d.exists()) {
    QString sline = i18n("Querying SLACK package list: ");
    kpackage->setStatus(sline);

    const QFileInfoList *list = d.entryInfoList();
    int count = list->count();
    QFileInfoListIterator it( *list );      // create list iterator
    QFileInfo *fi;                          // pointer for traversing

    kpackage->setPercent(0);
    int cnt = 0;
    while ( (fi=it.current()) ) {           // for each file...
      int n = (cnt*100)/count;
      if (!(n % 5))
	kpackage->setPercent(n);

      if (!fi->isDir() && fi->isReadable()) {
	fn = dr + fi->fileName();
	file = fopen(QFile::encodeName(fn),"r");
	if (file) {
          vb = QString::null;
	  while (fgets(linebuf,sizeof(linebuf),file)) {
	    if (strcmp(linebuf,FILELIST)) {
	      vb += linebuf;
	    } else {
	      break;
	    }
	  }
	  fclose(file);
	  p = collectInfo(vb.ascii(), INSTALLED);
	  if (p) {
	    smerge(p);
	    if (!p->pkgInsert(pki, typeID, TRUE))
	      delete p;
	  }
	}
      }
      cnt++;
      ++it;                          // goto next list element
    }
    kpackage->setPercent(100);
  }
}

//////////////////////////////////////////////////////////////////////////////
void SLACK::listPackList(QPtrList<packageInfo> *pki, QString s,  cacheObj *cp, int insState)
{
  int np;
  QString vb;
  char linebuf[1024];
  FILE *file;
  packageInfo *p;

  QString sline = i18n("Querying SLACK package list: ");
  sline += cp->location;

  kpackage->setStatus(sline);
  kpackage->setPercent(0);

  np = 0;
  file= fopen(QFile::encodeName(s), "r");
  vb = "";

  if (file) {
    while (fgets(linebuf,sizeof(linebuf),file)) {
      int len = strlen(linebuf);
      if (len > 1) {
	if (linebuf[len - 2] == '\r') {
	  linebuf[len - 2] = '\n';
	  linebuf[len - 1] = 0;
	}
      }
      if (strcmp(linebuf,"\n")) {
	vb += linebuf;
      } else if (vb != "") {
	p = collectInfo(vb.ascii(), insState);
	if (p) {
	  if (!p->pkgInsert(pki, typeID, insState == INITIAL, insState == INITIAL)) {
	    delete p;
	  }  else if (cp && insState != INITIAL) {
	    p->info->insert("base",new QString(cp->base));
	  }
	  if (p && insState == INITIAL) {
	    p->packageState = packageInfo::NOLIST;
	    p->info->remove("summary");
	  }
	}
	vb.truncate(0);
      }
    }
    fclose(file);
  }
  kpackage->setPercent(100);
}


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// mode: i = query installed    u = query uninstalled
packageInfo *SLACK::getPackageInfo(char mode, const QString &name, const QString &)
{
  char linebuf[1024];
  packageInfo *pki = 0;
  QString vb, search, fn;
  QString n,v;
  FILE *file;

  switch(mode) {
    ////////////////////////////////////////////////////////////////////////
    // query an installed package!
  case 'i':
    fn = DIR + name;
    file = fopen(QFile::encodeName(fn),"r");
    if (file) {
      vb = QString::null;
      while (fgets(linebuf,sizeof(linebuf),file)) {
	if (strcmp(linebuf,FILELIST)) {
	  vb += linebuf;
	} else {
	  break;
	}
      }
      fclose(file);
      pki = collectInfo(vb.ascii(), INSTALLED);
      if (pki) {
	smerge(pki);
      }
     }
    break;

    ////////////////////////////////////////////////////////////////////
    // query an uninstalled package
  case 'u':
    QFile f(name);
    if (f.exists()) {
      QDict<QString> *a = new QDict<QString>;
      a->setAutoDelete(TRUE);

      a->insert("group", new QString(i18n("OTHER")));
      a->insert("filename", new QString(name));

      QFileInfo f(name);
      a->insert("name", new QString(f.baseName()));

      QString *st = new QString("");
      st->setNum(f.size());
      a->insert("file-size", st);

      pki = new packageInfo(a,this);
      if (pki) {
	smerge(pki);
	pki->updated = TRUE;
      }
    }
    break;
  }
  return pki;
}


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
packageInfo *SLACK::collectInfo(const char *_inp, int insState)
{
  QString stmp, fn = "";
  QDict<QString> *a = new QDict<QString>;
  a->setAutoDelete(TRUE);

  char *str, *xstr;
  QString qstr;

  char *inp = qstrdup(_inp);
  str = strtok(inp,"\n");

  if (str) {
    do {
      if (str[0] == 0)
	break;

      xstr = strchr(str,':');
      if (xstr) {
	*xstr++ = 0;
	xstr++;
	while (*xstr == ' ') {
	  xstr++;
	}

	qstrcpy(str, QString(str).lower().ascii());
	if (*str == ' ')
	  str++;

	if (!strcmp("package name",str)) {
	  fn = xstr;
	  QString st = xstr;
	  if (st.right(4) == ".tgz")
	    a->insert("name", new QString(st.left(st.length() - 4)));
	  else
	    a->insert("name", new QString(st));
	} else if (!strcmp("package description",str)) {
	  int i = 0;
	  QString qstr = "";

	  while ((str = strtok(NULL,"\n"))) {
	    xstr = strchr(str,':');
	    if (xstr) {
	      *xstr++ = 0;
	      if (*(xstr) != 0)
		xstr++;
	      while (*xstr == ' ') {
		xstr++;
	      }
	      if (i == 0) {
		a->insert("summary", new QString(xstr));
	      } else {
		if (!strcmp(xstr,"") && (i != 1)) {
		  qstr += "\n";
		} else {
		  qstr += xstr;
		  qstr += " ";
		}
	      }
	    }
	    i++;
	  }
	  a->insert("description", new QString(qstr));
	} else if (!strcmp("package location",str)) {
	  QString sl = xstr;
	  if (insState != INSTALLED) {
	    int sls = sl.findRev("/");
	    if (sls >= 0) {
	      QRegExp num("[0-9][0-9]*");
	      int slf = sl.find(num,sls);
	      if (slf >= 0) {
		sls++;
		QString *gt = new QString(sl.mid(sls,slf-sls));
		if (trl->find(*gt)) {
		  *gt = *trl->find(*gt);
		}
		a->insert("group",gt);
	      }
	    }
	    sl = sl.right(sl.length() - 2);
	    sl += "/";
	    sl += fn;
	  }
	  if (insState == UNINSTALLED) {
	    a->insert("filename", new QString(sl));
	  }
	} else if (!strcmp("section",str)) {
	  a->insert("group", new QString(xstr));
	} else if (!strcmp("compressed package size",str) ||
		   !strcmp("package size (compressed)",str)) {
	  QString *stmp = new QString(xstr);
	  stmp->truncate(stmp->length() - 2);
	  *stmp += "000";
	  a->insert("file-size", stmp);
	} else if (!strcmp("uncompressed package size",str) ||
		   !strcmp("package size (uncompressed)",str)) {
	  QString *stmp = new QString(xstr);
	  stmp->truncate(stmp->length() - 2);
	  *stmp += "000";
	  a->insert("size", stmp);
	} else {
	  a->insert(str, new QString(xstr));
	}
      }
    } while ((str = strtok(NULL,"\n")));
  }

  delete [] inp;

  if (!a->find("name")) {
    delete a;
    return 0;
  } else {
    packageInfo *i = new packageInfo(a,this);
    i->packageState = packageInfo::INSTALLED;
    i->fixup();
    return i;
  }
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QStringList SLACK::getFileList(packageInfo *p)
{
  char linebuf[1024];
  QString st, fn;
  FILE *file;
  QString name;
  char mode;

  fn = p->getFilename();
  if(!fn.isEmpty())
    mode = 'u';
  else
    mode = 'i';

  QStringList filelist;

  switch(mode) {
      ////////////////////////////////////////////////////////////////////////
      // query an installed package!
    case 'i':
      name = p->getProperty("name");

      fn = DIR + name;
      file = fopen(QFile::encodeName(fn),"r");
      if (file) {
	while (fgets(linebuf,sizeof(linebuf),file)) {
	  if (!strcmp(linebuf,FILELIST)) {
	    break;
	  }
	}
	while (fgets(linebuf,sizeof(linebuf),file)) {
	  st = "/";
	  st += linebuf;
	  st.truncate(st.length() -1);
	  if (st.left(8) != "/install") {
	    filelist.append(st);
	  }
	}
	fclose(file);
      }
      break;

      ////////////////////////////////////////////////////////////////////
      // query an uninstalled package
    case 'u':
           name = fn;

	   QString s = "sh -c 'cat ";
	   s += fn;
	   s += "|gunzip |tar -t -f -'";

	   filelist =  kpty->run(s);
      break;
    }

  return filelist;
}

//////////////////////////////////////////////////////////////////////////////
// Call the script to uninstall packages setting parameters
// to slack dependent on flags, returning whether everyting worked
//////////////////////////////////////////////////////////////////////////////
QString SLACK::doUninstall(int uninstallFlags, QString packs, bool &)
{
  QString s = "removepkg ";
  s += setOptions(uninstallFlags, paramsUninst);
  s +=  packs;

  kdDebug() << "uCMD=" << s << "\n";

  return  s;
}

//////////////////////////////////////////////////////////////////////////////
// Call the script to install packages setting parameters
// to slack dependent on flags, returning whether everyting worked
//////////////////////////////////////////////////////////////////////////////
QString SLACK::install(int installFlags, QPtrList<packageInfo> *plist, bool &test)
{
  packageInfo *pk;
  int i = 0;
  QString packs = "";
  for (pk = plist->first(); pk != 0; pk = plist->next()) {
    QString fname = pk->fetchFilename();
    if (fname != "") {
      packs += fname + " ";
      i++;
    }
  }
  return doInstall(installFlags, packs, test);
}

QString SLACK::doInstall(int installFlags, QString packs, bool &)
{

 QString s = "installpkg ";
 s += setOptions(installFlags, paramsInst);
 s +=  packs;

  kdDebug() << "iCMD=" << s << "\n";

  return  s;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QStringList SLACK::FindFile(const QString &name)
{
  FILE *file;
  char linebuf[1024];
  QString buf, st;
  QString fn, dr = DIR;
  QStringList filelist;

  QDir d(DIR);
  if (d.exists()) {
    QString sline = i18n("Querying SLACK package list: ");
    kpackage->setStatus(sline);

    const QFileInfoList *list = d.entryInfoList();
    int count = list->count();
    QFileInfoListIterator it( *list );      // create list iterator
    QFileInfo *fi;                          // pointer for traversing

    kpackage->setPercent(0);
    int cnt = 0;
    while ( (fi=it.current()) ) {           // for each file...
      int n = (cnt*100)/count;
      if (!(n % 5))
	kpackage->setPercent(n);

      if (!fi->isDir() && fi->isReadable()) {
	fn = dr + fi->fileName();
	file = fopen(QFile::encodeName(fn),"r");
	if (file) {
	  while (fgets(linebuf,sizeof(linebuf),file)) {
	    if (!strcmp(linebuf,FILELIST)) {
	      break;
	    }
	  }
	  while (fgets(linebuf,sizeof(linebuf),file)) {
	    if (QString::fromLocal8Bit(linebuf).find(name) != -1) {
	      st = "/";
	      st += linebuf;
	      st.truncate(st.length() -1);
	      if (st.left(8) != "/install") {
		QString s = fi->fileName();
		s += "\t";
		s += st;
		filelist.append(s);
	      }
	    }
	  }
	  fclose(file);
	}
      }
      cnt++;
      ++it;                          // goto next list element
    }
  }
  return filelist;
}


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void SLACK::setLocation()
{
    locatedialog->restore();
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void SLACK::setAvail(LcacheObj *slist)
{
  if (packageLoc)
    delete packageLoc;
  packageLoc = slist;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

void SLACK::smerge(packageInfo *p)
{
  p->smerge(typeID);
}
#include "slackInterface.moc"
