Logo Search packages:      
Sourcecode: paco version File versions  Download package

pkgset.cc

//=======================================================================
// pkgset.cc
//-----------------------------------------------------------------------
// This file is part of the package paco
// Copyright (C) 2004-2009 David Rosal
// For more information visit http://paco.sourceforge.net
//=======================================================================

#include "config.h"
#include "global.h"
#include "pkgset.h"
#include "options.h"
#include "pkg.h"
#include "paco/dir.h"
#include <fstream>
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
#include <iomanip>
#include <vector>

#if !HAVE_STRCASECMP
#     define strcasecmp(a,b)  strcmp(a,b)
#endif

using std::string;
using std::vector;
using std::cout;
using std::endl;
using std::setw;
using std::for_each;
using std::mem_fun;
using namespace Paco;


// Forward decls
static bool matchPkg(string const& s, string const& pkg);


PkgSet::PkgSet()
:
      mSizeInst(0),
      mSizeMiss(0),
      mFilesInst(0),
      mFilesMiss(0)
{ }


PkgSet::PkgSet(vector<string> const& args)
:
      mSizeInst(0),
      mSizeMiss(0),
      mFilesInst(0),
      mFilesMiss(0)
{
      getPkgs(args);
}


// [virtual]
PkgSet::~PkgSet()
{
      for (iterator p = begin(); p != end(); ++p) {
            assert(*p != NULL);
            if (*p) {
                  delete *p;
                  *p = NULL;
            }
      }
}


void PkgSet::update(bool allPkgs)
{
      if (allPkgs && Config::version().find("1") == 0) {
            gOut.vrb("Detected version 1 database. Upgrading to version 2...\n");
            for_each(begin(), end(), mem_fun(&Pkg::upgradeLog));
            string infoDir(Config::logdir() + "/_info");
            if (!rmdir(infoDir.c_str()))
                  gOut.vrb("Directory " + infoDir + " has been removed\n");
            else if (errno != ENOENT)
                  gOut.vrb("rmdir(\"" + infoDir + "\")", errno);
      }
      
      for_each(begin(), end(), mem_fun(&Pkg::update));
      if (allPkgs)
            Config::touchStamp();
}


void PkgSet::remove(Options& opt)
{
      for (iterator p = begin(); p != end(); ++p)
            (*p)->remove(opt);
}


void PkgSet::unlog()
{
      for_each(begin(), end(), mem_fun(&Pkg::unlog));
}


void PkgSet::printConfOpts()
{
      for (iterator p = begin(); p != end(); ++p)
            (*p)->printConfOpts(size() > 1);
}


void PkgSet::printInfo()
{
      for_each(begin(), end(), mem_fun(&Pkg::printInfo));
}


void PkgSet::getFiles(int fType /* = Pkg::ALL_FILES */)
{
      for (iterator p = begin(); p != end(); ++p)
            (*p)->getFiles(fType);
}


void PkgSet::getAllPkgs()
{
      Dir dir(Config::logdir());
      string name;

      while (dir.read(name)) {
            try { add(new Pkg(name)); }
            catch (Pkg::ConstructorError&) { }
      }

      if (empty())
            gOut.vrb("paco: No packages logged in directory " + Config::logdir() + "\n");
}


//
// Get all logged packages that match any name in 'args'.
//
void PkgSet::getPkgs(vector<string> const& args)
{
      Dir dir(Config::logdir());
      bool logged;
      string name;
            
      for (uint i = 0; i < args.size(); ++i, dir.rewind()) {
            if (!isalnum(args[i][0]) && args[i][1]) {
                  gExitStatus = EXIT_FAILURE;
                  gOut.vrb("paco: " + args[i] + ": invalid package name\n");
                  continue;
            }
            for (logged = false; dir.read(name); ) {
                  if (matchPkg(args[i], name)) {
                        try {
                              add(new Pkg(name));
                              logged = true;
                        }
                        catch (Pkg::ConstructorError&) { }
                  }
            }
            if (!logged) {
                  gExitStatus = EXIT_FAILURE;
                  gOut.vrb("paco: " + args[i] + ": package not logged\n");
            }
      }
}


void PkgSet::queryFiles(vector<string> const& args)
{
      gExitStatus = EXIT_FAILURE;

      getFiles();
      
      for (uint i = 0; i < args.size(); ++i) {
            string real = realDir(args[i]);
            cout << real << ":";
            for (iterator p = begin(); p != end(); ++p) {
                  if ((*p)->hasFile(real)) {
                        gExitStatus = EXIT_SUCCESS;
                        cout << '\t' << (*p)->name();
                  }
            }
            cout << "\n";
      }
}


void PkgSet::add(Pkg* pkg)
{
      push_back(pkg);
      mSizeInst += pkg->sizeInst();
      mSizeMiss += pkg->sizeMiss();
      mFilesInst += pkg->filesInst();
      mFilesMiss += pkg->filesMiss();
}


//
// Print the packages in an ls' style
//
void PkgSet::lsPkgs()
{
      size_t maxlen = 0, cols;
      iterator p;

      for (p = begin(); p != end(); ++p)
            maxlen = std::max(maxlen, (*p)->name().size());
      maxlen += 2;
      
      if (!(cols = Out::screenWidth() / maxlen))
            cols = 1;
      int rows = size() / cols + ((size() % cols) != 0);

      if (rows == 1) {
            uint cnt = 0;
            for (p = begin(); p != end(); ++p)
                  cout << (cnt++ ? "  " : "") << (*p)->name();
            cout << endl;
            return;
      }

      for (int k = 0; k < rows; ++k) {
            for (p = begin() + k; p < end(); p += rows)
                  cout << (*p)->name() << setw(maxlen - (*p)->name().size()) << " ";
            cout << endl;
      }
}


void PkgSet::listPkgs(Options& opt)
{
      assert(empty() == false);

      std::sort(begin(), end(), Sorter(opt.sortType()));
      if (opt.reverse())
            std::reverse(begin(), end());

      if (opt.oneColumn())
            transform(begin(), end(), begin(), Pkg::Lister(opt, *this));
      else
            lsPkgs();
}


void PkgSet::listFiles(Options& opt)
{
      assert(empty() == false);

      int type = opt.filesInst() ? Pkg::INSTALLED_FILES : 0;
      if (opt.filesMiss())
            type |= Pkg::MISSING_FILES;

      getFiles(type);

      transform(begin(), end(), begin(), Pkg::FileLister(opt, *this));
}


//----------------//
// PkgSet::Sorter //
//----------------//


PkgSet::Sorter::Sorter(SortType type)
:
      mSortFunc()
{
      switch (type) {
            case SORT_SIZE_INST:
                  mSortFunc = &Sorter::sortBySizeInst;
                  break;
            case SORT_SIZE_MISS:
                  mSortFunc = &Sorter::sortBySizeMiss;
                  break;
            case SORT_FILES_INST:
                  mSortFunc = &Sorter::sortByFilesInst;
                  break;
            case SORT_FILES_MISS:
                  mSortFunc = &Sorter::sortByFilesMiss;
                  break;
            case SORT_DATE:
                  mSortFunc = &Sorter::sortByDate;
                  break;
            default:
                  mSortFunc = &Sorter::sortByName;
      }
}


inline bool PkgSet::Sorter::operator()(Pkg* left, Pkg* right) const
{
      return (this->*mSortFunc)(right, left);
}

inline bool PkgSet::Sorter::sortByName(Pkg* left, Pkg* right) const
{
      return strcasecmp(right->name().c_str(), left->name().c_str()) < 0;
}

inline bool PkgSet::Sorter::sortBySizeInst(Pkg* left, Pkg* right) const
{
      return left->sizeInst() > right->sizeInst();
}

inline bool PkgSet::Sorter::sortBySizeMiss(Pkg* left, Pkg* right) const
{
      return left->sizeMiss() > right->sizeMiss();
}

inline bool PkgSet::Sorter::sortByFilesMiss(Pkg* left, Pkg* right) const
{
      return left->filesMiss() > right->filesMiss();
}

inline bool PkgSet::Sorter::sortByFilesInst(Pkg* left, Pkg* right) const
{
      return left->filesInst() > right->filesInst();
}

inline bool PkgSet::Sorter::sortByDate(Pkg* left, Pkg* right) const
{
      return left->date() > right->date();
}


//--------------//
// Static funcs //
//--------------//

//
// Check whether the string 's' is a valid reference to the 'pkg'.
// Example: given the pkg 'foo-bar-1.0', the string 'foo-bar' matches,
// but not 'foo' or 'foo-bar-2.0'.
// This provides a kind of automatic pkg name completion for the command
// line arguments, whose targets are the names of the logged pkgs.
//
static bool matchPkg(string const& s, string const& pkg)
{
      string sBase = Pkg::getBase(s);
      string pkgBase = Pkg::getBase(pkg);
      string sVersion = Pkg::getVersion(s);
      string pkgVersion = Pkg::getVersion(pkg);
      
      if (Config::caseSensitive()) {
            if (sBase != pkgBase)
                  return false;
      }
      else if (strcasecmp(pkgBase.c_str(), sBase.c_str()))
            return false;

      if (sVersion.empty() || sVersion == pkgVersion)
            return true;

      else if (sVersion.compare(0, sVersion.size(), pkgVersion.c_str(),
            sVersion.size()))
            return false;

      return ispunct(pkgVersion.at(sVersion.size()));
}



Generated by  Doxygen 1.6.0   Back to index