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

pkg.cc

//=======================================================================
// pkg.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 "pkg.h"
#include "pkgset.h"
#include "options.h"
#include <string>
#include <iostream>
#include <iomanip>
#include <fstream>

using std::endl;
using std::cout;
using std::string;
using std::setw;
using namespace Paco;

// Forward decls
static void cutPrint(string);
static void removeDir(string const&);
static void removeParentDir(string const&);
static int getDigits(long);
template <typename T, typename U> static T max(T const&, U const&);


Pkg::Pkg(string const& aName)
:
      BasePkg(aName)
{ }


// [virtual]
Pkg::~Pkg()
{ }


void Pkg::unlog() const
{
      if (!access(mLog.c_str(), W_OK)) {
            if (!unlink(mLog.c_str()))
                  gOut.vrb("Package " + mName + " removed from the database\n");
            else
                  gOut.vrb("unlink(" + mLog + ")", errno);
      }
}


void Pkg::printInfo() const
{
      FileStream<std::ifstream> f(mLog);
      string buf;
      bool printed(false);
      
      string line(mName.size() + 2, '-');
      cout << line << "\n " << mName << " \n" << line << "\n" << endl;

      while (getline(f, buf) && buf[0] == '#') {
            if (buf == "#:Description")
                  cout << "Description:\n" << endl;
            else if (!buf.find("#:")) {
                  cutPrint(buf.substr(2));
                  printed = true;
            }
      }

      if (printed)
            cout << endl;
}


bool Pkg::update() const
{
      gOut.vrb("Updating " + mName + "... ");
      bool ret = updateLog(mLog);
      gOut.vrb(ret ? "ok\n" : "no files logged (removed)\n");
      return ret;
}


void Pkg::upgradeLog() const
{
      std::ifstream fLog(mLog.c_str());
      if (!fLog)
            return;

      string buf;

      getline(fLog, buf);
      if (!buf.find("#!paco-2."))
            return;
      
      std::ostringstream s;
      s << "#!paco-" PACKAGE_VERSION "\n";

      string::size_type p = buf.rfind(" ");
      if (p != string::npos)
            s << "#d:" << str2num<int>(buf.substr(p)) << '\n';

      string infoLog = Config::logdir() + "/_info/" + mName;
      std::ifstream fInfoLog(infoLog.c_str());
      if (fInfoLog) {
            getline(fInfoLog, buf);
            while (getline(fInfoLog, buf))
                  s << (buf[0] == '#' ? "" : "#:") << buf << '\n';
      }

      while (getline(fLog, buf)) {
            if (buf[0] != '#' || !fInfoLog)
                  s << buf << '\n';
      }

      unlink(infoLog.c_str());

      fLog.close();
      FileStream<std::ofstream> fLog2(mLog);
      fLog2 << s.str();
}


long Pkg::countShared(PkgSet& all)
{
      long cnt(0);
      for (iterator f = begin(); f != end(); ++f)
            cnt += shares(*f, all);
      return cnt;
}


void Pkg::printConfOpts(bool printPkgName) const
{
      if (printPkgName)
            cout << mName << ":\n";
      if (mConfOpts.size())
            cout << mConfOpts << endl;
      if (printPkgName)
            cout << endl;
}


void Pkg::remove(Options& opt)
{
      if (!opt.batch()) {
            cout << "Remove package " << mName << " (y/N) ? ";
            string buf;
            if (!(getline(std::cin, buf) && (buf == "y" || buf == "yes")))
                  return;
      }
      
      PkgSet all;
      if (!opt.removeShared()) {
            all.getAllPkgs();
            all.getFiles();
      }

      {
            Out::Silencer s;
            update();
      }

      getFiles();

      struct stat s;
      bool keepLog(false);

      for (iterator f = begin(); f != end(); ++f) {
            if (lstat((*f)->name().c_str(), &s) < 0)
                  continue;
            else if (inPaths((*f)->name(), opt.skip())) {
                  gOut.vrb((*f)->name() + ": skipped\n");
                  keepLog = true;
            }
            else if (!opt.removeShared() && shares(*f, all)) {
                  gOut.vrb((*f)->name() + ": shared\n");
                  keepLog = true;
            }
            else if (!unlink((*f)->name().c_str())) {
                  if (S_ISLNK(s.st_mode))
                        gOut.vrb("Removed symlink '"  + (*f)->name() + "'\n");
                  else
                        gOut.vrb("Removed '"  + (*f)->name() + "'\n");
                  removeParentDir((*f)->name());
            }
            else {
                  gOut.vrb("unlink(" + (*f)->name() + ")", errno);
                  keepLog = true;
            }
      }
      
      if (opt.unlog() || !keepLog)
            unlog();
      else {
            Out::Silencer silencer;
            update();
      }
}


// [static]
string Pkg::getVersion(string const& name)
{
      for (string::size_type i = 1; i < name.size(); ++i) {
            if (isdigit(name.at(i)) && name.at(i - 1) == '-')
                  return name.substr(i);
      }
      return "";
}


// [static]
string Pkg::getBase(string const& name)
{
      bool dash(false);
      string::size_type i;

      for (i = 1; i < name.size(); ++i) {
            if (name.at(i) == '-')
                  dash = true;
            else if (dash) {
                  if (isdigit(name.at(i)))
                        break;
                  dash = false;
            }
      }

      return dash ? name.substr(0, i - 1) : name;
}


//-------------//
// Pkg::Lister //
//-------------//


Pkg::Lister::Lister(Options& __opt, PkgSet& __set)
:
      mOpt(__opt),
      mSet(__set),
      mAllPkgs(),
      mSizeInstWidth(mOpt.size() ? getSizeInstWidth() : 0),
      mSizeMissWidth(mOpt.sizeMiss() ? getSizeMissWidth() : 0),
      mFilesInstWidth(mOpt.filesInst() ? getFilesInstWidth() : 0),
      mFilesMissWidth(mOpt.filesMiss() ? getFilesMissWidth() : 0),
      mFilesSharedWidth(mOpt.filesShared() ?
            getDigits(mSet.filesInst() + mSet.filesMiss()) : 0)
{
      if (mOpt.filesShared()) {
            mAllPkgs.getAllPkgs();
            mAllPkgs.getFiles();
            mSet.getFiles();
      }
}     


Pkg::Lister::~Lister()
{
      if (!mOpt.total())
            return;

      if (mOpt.size())
            printSizeInst(mSet.sizeInst());
      if (mOpt.sizeMiss())
            printSizeMiss(mSet.sizeMiss());
      if (mOpt.filesInst())
            printFilesInst(mSet.filesInst());
      if (mOpt.filesMiss())
            printFilesMiss(mSet.filesMiss());
      if (mOpt.filesShared())
            cout << setw(mFilesSharedWidth + 4) << " ";
      if (mOpt.day())
            printDate(0);
            
      cout << "Total" << endl;
}


int Pkg::Lister::getSizeMissWidth()
{
      int n = mOpt.total() ? toString(mSet.sizeMiss(), mOpt.sizeUnit()).size() : 0;
      for (PkgSet::iterator p = mSet.begin(); p != mSet.end(); ++p)
            n = max(n, toString((*p)->sizeMiss(), mOpt.sizeUnit()).size());
      return n;
}


int Pkg::Lister::getSizeInstWidth()
{
      int n = mOpt.total() ? toString(mSet.sizeInst(), mOpt.sizeUnit()).size() : 0;
      for (PkgSet::iterator p = mSet.begin(); p != mSet.end(); ++p)
            n = max(n, toString((*p)->sizeInst(), mOpt.sizeUnit()).size());
      return n;
}


int Pkg::Lister::getFilesInstWidth()
{
      if (mOpt.total())
            return getDigits(mSet.filesInst());
      int n = 0;
      for (PkgSet::iterator p = mSet.begin(); p != mSet.end(); ++p)
            n = max(n, (*p)->filesInst());
      return getDigits(n);
}


int Pkg::Lister::getFilesMissWidth()
{
      if (mOpt.total())
            return getDigits(mSet.filesMiss());
      int n = 0;
      for (PkgSet::iterator p = mSet.begin(); p != mSet.end(); ++p)
            n = max(n, (*p)->filesMiss());
      return getDigits(n);
}


void Pkg::Lister::printDate(int __time) const
{
      char dat[32] = "";
      struct tm* t;
      time_t timeT = static_cast<time_t>(__time);

      if (!__time || !(t = localtime(&timeT)))
            snprintf(dat, sizeof(dat), "%*c", mOpt.hour() ? 17 : 11, ' ');
      else if (mOpt.hour())
            strftime(dat, sizeof(dat) - 1, "%d-%b-%Y %H:%M", t);
      else
            strftime(dat, sizeof(dat) - 1, "%d-%b-%Y", t);
            
      cout << dat << "  ";
}


void Pkg::Lister::printFilesInst(long n) const
{
      cout << setw(mFilesInstWidth) << n
            << ((mOpt.filesMiss() || mOpt.filesShared()) ? " " : "  ");
}


template <typename T>   // T = {float,long}
void Pkg::Lister::printSizeInst(T size) const
{
      cout << setw(mSizeInstWidth) << toString(size, mOpt.sizeUnit())
            << (mOpt.sizeMiss() ? " " : "  ");
}


template <typename T>   // T = {float,long}
void Pkg::Lister::printSizeMiss(T size) const
{
      cout << "[" << setw(mSizeMissWidth)
            << (size ? toString(size, mOpt.sizeUnit()) : " ") << "]  ";
}


void Pkg::Lister::printFilesMiss(long n) const
{
      cout << "[" << setw(mFilesMissWidth);
      if (n)
            cout << n << "] ";
      else
            cout << " " << "] ";
      if (!mOpt.filesShared())
            cout << " ";
}


void Pkg::Lister::printFilesShared(long n) const
{
      cout << "(" << setw(mFilesSharedWidth);
      if (n)
            cout << n << ")  ";
      else
            cout << " " << ")  ";
}


Pkg* Pkg::Lister::operator()(Pkg* pkg)
{
      if (mOpt.size())
            printSizeInst(pkg->sizeInst());
      if (mOpt.sizeMiss())
            printSizeMiss(pkg->sizeMiss());
      if (mOpt.filesInst())
            printFilesInst(pkg->filesInst());
      if (mOpt.filesMiss())
            printFilesMiss(pkg->filesMiss());
      if (mOpt.filesShared())
            printFilesShared(pkg->countShared(mAllPkgs));
      if (mOpt.day())
            printDate(pkg->date());
            
      cout << pkg->name() << endl;

      return pkg;
}


//-----------------//
// Pkg::FileLister //
//-----------------//


Pkg::FileLister::FileLister(Options& __opt, PkgSet& __set)
:
      mOpt(__opt),
      mSet(__set),
      mSizeWidth(0),
      mPrintTotal(mOpt.total() && mOpt.size()),
      mTotalSize(0),
      mAllPkgs(),
      mPkgCnt(0),
      mFileCnt(0)
{
      if (mOpt.size()) {
            mTotalSize = mOpt.filesInst() ? mSet.sizeInst() : 0;
            if (mOpt.filesMiss())
                  mTotalSize += mSet.sizeMiss();
            mSizeWidth = getSizeWidth();
      }

      if (mOpt.filesShared() || mOpt.filesNonShared()) {
            mTotalSize = 0;
            mAllPkgs.getAllPkgs();
            mAllPkgs.getFiles();
      }
}


Pkg::FileLister::~FileLister()
{
      if (mPrintTotal && mFileCnt) {
            cout << endl << setw(mSizeWidth)
                  << (mTotalSize < 0 ? "0" : toString(mTotalSize, mOpt.sizeUnit()))
                  << "  Total" << endl;
      }
}


int Pkg::FileLister::getSizeWidth()
{
      int n = mOpt.total() ? toString(mTotalSize, mOpt.sizeUnit()).size() : 0;
      for (PkgSet::iterator p = mSet.begin(); p != mSet.end(); ++p) {
            for (Pkg::iterator f = (*p)->begin(); f != (*p)->end(); ++f)
                  n = max(n, toString((*f)->size(), mOpt.sizeUnit()).size());
      }
      return n;
}


Pkg* Pkg::FileLister::operator()(Pkg* pkg)
{
      if (mOpt.filesShared() || mOpt.filesNonShared()) {
            if (!mOpt.noPkgName())
                  cout << pkg->name() << ":" << endl;
            if (mOpt.filesShared())
                  listSharedFiles(pkg);
            else
                  listNonSharedFiles(pkg);
      }
      else
            listFiles(pkg);

      if (++mPkgCnt < mSet.size() && !mOpt.noPkgName())
            cout << endl;

      return pkg;
}


inline void Pkg::FileLister::printFile(File* file)
{
      if (mOpt.size())
            cout << setw(mSizeWidth) << toString(file->size(), mOpt.sizeUnit()) << "  ";

      cout << file->name();

      if (file->symlink() && mOpt.symlinks()) {
            char ln[4096];
            int cnt = readlink(file->name().c_str(), ln, sizeof(ln) - 1);
            if (cnt > 0)
                  ln[cnt] = 0;
            else
                  memcpy(ln, "?", 2);
            cout << " -> " << ln;
      }
      
      cout << "\n";

      ++mFileCnt;
}


void Pkg::FileLister::listFiles(Pkg* pkg)
{
      if (!mOpt.noPkgName())
            cout << pkg->name() << ":" << endl;

      pkg->sort(mOpt.sortType(), mOpt.reverse());

      for (iterator f = pkg->begin(); f != pkg->end(); ++f)
            printFile(*f);
}


void Pkg::FileLister::listSharedFiles(Pkg* pkg)
{
      for (PkgSet::iterator p = mAllPkgs.begin(); p != mAllPkgs.end(); ++p) {
            if ((*p)->name() == pkg->name())
                  continue;
            uint cnt = 0;
            for (iterator f = pkg->begin(); f != pkg->end(); ++f) {
                  if (LIKELY(!(*p)->hasFile(*f)))
                        continue;
                  else if (mOpt.whoShares()) {
                        if (!cnt++)
                              cout << "(" << (*p)->name() << ")" << endl;
                  }
                  else if (!(*f)->shared())
                        (*f)->shared(true); // do not repeat files
                  else
                        continue;
                  
                  printFile(*f);
                  mTotalSize += (*f)->size();
            }
      }
}     


void Pkg::FileLister::listNonSharedFiles(Pkg* pkg)
{
      for (iterator f = pkg->begin(); f != pkg->end(); ++f) {
            if (LIKELY(!pkg->shares(*f, mAllPkgs))) {
                  printFile(*f);
                  mTotalSize += (*f)->size();
            }
      }
}     


//-------------------//
// static free funcs //
//-------------------//


template <typename T, typename U>
inline static T max(T const& a, U const& b)
{
      return static_cast<size_t>(a) > static_cast<size_t>(b) ? a : b;
}


//
// Return the number of digits of a number
//
static int getDigits(long n)
{
    int ret;
    for (ret = 0; n; n /= 10, ret++) ;
    return ret;
}


static void removeParentDir(string const& path)
{
      string dir(path);
      string::size_type i = dir.size() - 1;

      // Remove trailing slashes
      while (i > 0 && dir.at(i) == '/')
            dir.erase(i--);
      
      // Get the parent dir and remove it
      if ((i = dir.rfind('/'))) {
            if (i != string::npos)
                  dir.erase(i);
            removeDir(dir);
      }
}


static void removeDir(string const& dir)
{
      if (!rmdir(dir.c_str())) {
            gOut.vrb("Removed directory '" + dir + "'\n");
            removeParentDir(dir);
      }
      else if (errno != ENOTEMPTY)
            gOut.vrb("rmdir(" + dir + ")", errno);
}


//
// This function takes a string and prints it out in pieces not
// longer than a given maximmum length. The cut points are not in the middle
// of words if possible.
//
static void cutPrint(string buf)
{
      uint width = Out::screenWidth();

      // If the buffer is short enough, just print it and return
      if (buf.size() <= width) {
            cout << buf << endl;
            return;
      }

      string out;
      bool  beggining = true, // Leading spaces indicator
                  spaces = false;   // True if there are any space to cut at
 
      for (uint j, i = 0, len = 0; i < buf.size(); ) {

            if (buf.at(i) == ' ')
                  spaces = !beggining;
            else 
                  beggining = false;
            
            if (len < width) {
                  ++len;
                  out += buf.at(i++);
                  continue;
            }        
            // len >= width
            else if (spaces && !beggining) {
                  for (j = out.size(); j && i && buf.at(i) != ' '; --j, --i) ;
                  ++i;
                  out.erase(j);
        }

            for ( ; i < buf.size() && buf.at(i) == ' '; ++i) ;

            cout << out << endl;
            out.clear();
            len = 0;
            spaces = false;
    }
      
      // The remaining piece
      cout << out << endl;
}


Generated by  Doxygen 1.6.0   Back to index