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

basepkg.cc

//=======================================================================
// basepkg.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 "common.h"
#include "file.h"
#include "baseconfig.h"
#include "basepkg.h"
#include <cstring>
#include <fstream>
#include <algorithm>
#include <cctype>

// For *BSD statfs()
#if HAVE_SYS_PARAM_H
#     include <sys/param.h>
#endif
#if HAVE_SYS_MOUNT_H
#     include <sys/mount.h>
#endif

#if (HAVE_SYS_STATVFS_H && HAVE_STATVFS)
#     define STATFS statvfs
#     include <sys/statvfs.h>
#elif HAVE_STATFS
#     define STATFS statfs
#     if HAVE_SYS_VFS_H
#           include <sys/vfs.h>
#     elif HAVE_SYS_STATFS
#           include <sys/statfs.h>
#     endif
#elif STATFS
#     undef STATFS
#endif

using std::string;
using namespace Paco;


BasePkg::BasePkg(string const& __name)
:
      mName(__name),
      mLog(BaseConfig::logdir() + "/" + mName),
      mDate(0),
      mSizeInst(0),
      mSizeMiss(0),
      mFilesInst(0),
      mFilesMiss(0),
      mSortType(NO_SORT),
      mSortReverse(false),
      mConfOpts()
{
      if (mName.empty() || ispunct(mName.at(0)))
            throw ConstructorError();
      
      // Check for the '#!paco' header (security issue)
      std::ifstream f(log().c_str());
      string buf;
      if (!(f && getline(f, buf) && buf.find("#!paco") == 0))
            throw ConstructorError();
      
      while (getline(f, buf) && buf.size() > 3 && buf[0] == '#' &&
            buf[2] == ':') {
            switch (buf[1]) {
                  case 'd':
                        mDate = str2num<int>(buf.substr(3));
                        break;
                  case '#':
                        sscanf(buf.c_str(), "##:%ld|%ld|%ld|%ld",
                              &mSizeInst, &mSizeMiss, &mFilesInst, &mFilesMiss);
                        break;
                  case 'c':
                        mConfOpts = buf.substr(3);
                        return;
            }
      }
}


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


void BasePkg::getFiles(int type /* = ALL_FILES */)
{
      clear();

      string buf;
      FileStream<std::ifstream> f(mLog);

      // skip the header
      while (getline(f, buf) && buf[0] == '#') ;
      if (f.eof())
            return;
      
      char path[4096];
      long raw, bz2, gz, s;
      
      // Read files 
      do {
            if (UNLIKELY(4 != sscanf(buf.c_str(), "%[^|]|%ld|%ld|%ld",
                  path, &raw, &gz, &bz2))) {
                  goto ____parse_error;
            }
            switch (path[0]) {
                  case '/':
                        if (type & INSTALLED_FILES) {
                              if (raw != File::SIZEOF_MISSING)
                                    push_back(new File(path, raw));
                              if (gz != File::SIZEOF_MISSING)
                                    push_back(new File(string(path) + ".gz", gz));
                              if (bz2 != File::SIZEOF_MISSING)
                                    push_back(new File(string(path) + ".bz2", bz2));
                        }
                        break;
                  case '-':
                        if (type & MISSING_FILES) {
                              s = (raw != File::SIZEOF_MISSING) ? raw : 0 +
                                    (gz != File::SIZEOF_MISSING) ? gz : 0 +
                                    (bz2 != File::SIZEOF_MISSING) ? bz2 : 0;
                              push_back(new File(&path[1], s, File::MISSING));
                        }
                        break;
                  default: ____parse_error:
                        throw X("Parse error while reading " + mLog
                              + "\nRun 'paco -au' to update the database");
            }
      }
      while (getline(f, buf));
}



bool BasePkg::hasFile(File* file)
{
      sort();
      return std::binary_search(begin(), end(), file, Sorter());
}


void BasePkg::sort(     SortType type,    // = SORT_NAME
                              bool reverse)     // = false
{
      // sort only if the list is not already sorted
      if (mSortType != type) {
            std::sort(begin(), end(), Sorter(type));
            mSortType = type;
      }
      // reverse only if needed
      if (mSortReverse != reverse) {
            std::reverse(begin(), end());
            mSortReverse = reverse;
      }
}


//
// Update the log file @log.
// IMPORTANT: If return value is false it means that the log does not
// exist or it was empty and it has been removed.
//
// [static]
bool BasePkg::updateLog(string const& log)
{
      if (access(log.c_str(), F_OK) < 0)
            return false;

      FileStream<std::ifstream> f(log);
      string buf;
      if (!(getline(f, buf) && !buf.find("#!paco")))
            return true;

      // header
      std::ostringstream s;
      while (getline(f, buf) && buf[0] == '#') {
            if (buf.size() > 1 && buf[1] != '#')
                  s << buf << "\n";
      }
      if (f.eof()) {    // empty log (no logged files, only header)
            unlink(log.c_str());
            return false;
      }
      
      char* p;
      char* file;
      enum { RAW, GZ, BZ2, NSIZES };
      long size[NSIZES], sizeInst = 0, sizeMiss = 0, filesInst = 0, filesMiss = 0;
      
      do {
            if (UNLIKELY(!(file = strchr(const_cast<char*>(buf.c_str()), '/'))))
                  continue;
            else if ((p = strchr(file, '|')))
                  *p++ = 0;
            if (getSize(size[RAW], file) +
                  getSize(size[GZ], file, ".gz") +
                  getSize(size[BZ2], file, ".bz2")) {
                  // installed file
                  s << file;
                  for (uint i = 0; i < NSIZES; ++i) {
                        s << "|" << size[i];
                        if (size[i] > 0)
                              sizeInst += size[i];
                  }
                  s << "\n";
                  filesInst++;
            }
            else if (p && LIKELY(3 == sscanf(p, "%ld|%ld|%ld", &size[RAW], &size[GZ], &size[BZ2]))) {
                  // missing file
                  s << "-" << file;
                  for (uint i = 0; i < NSIZES; ++i) {
                        s << "|" << size[i];
                        if (size[i] > 0)
                              sizeMiss += size[i];
                  }
                  s << "\n";
                  filesMiss++;
            }
            else {
                  // missing file with unknown sizes
                  s << "-" << file << "|-2|-2|-2\n";
                  filesMiss++;
            }
      }
      while (getline(f, buf));
      
      f.close();
      FileStream<std::ofstream> f2(log);
      f2  << "#!paco-" PACKAGE_VERSION "\n##:" << sizeInst << "|" << sizeMiss
            << "|" << filesInst << "|" << filesMiss << "\n" << s.str();
      
      return true;
}


// [static]
bool BasePkg::getSize(long& size, string const& base, string const& suffix)
{
      string path(base + suffix);
      struct stat s;

      if (lstat(path.c_str(), &s) < 0)
            size = File::SIZEOF_MISSING;
      else if (S_ISREG(s.st_mode)) {
#if STATFS
            struct STATFS f;
#endif
            int bsize;
            if (BaseConfig::blockSize())
                  bsize = BaseConfig::blockSize();
#if STATFS
            else if (LIKELY(!STATFS(path.c_str(), &f) && (f.f_bsize > 0)))
                  bsize = f.f_bsize;
#endif
            else
                  bsize = s.st_blksize;

            size = ((s.st_size / bsize) + ((s.st_size % bsize) > 0)) * bsize;
      }
      else if (S_ISLNK(s.st_mode))
            size = File::SIZEOF_SYMLINK;
      
      return (size != File::SIZEOF_MISSING);
}


//-----------------//
// BasePkg::Sorter //
//-----------------//


BasePkg::Sorter::Sorter(SortType type /* = SORT_NAME */)
:
      mSortFunc(type == SORT_NAME ? &Sorter::sortByName : &Sorter::sortBySize)
{ }


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


inline bool BasePkg::Sorter::sortByName(File* left, File* right) const
{
      return left->name() < right->name();
}


inline bool BasePkg::Sorter::sortBySize(File* left, File* right) const
{
      return left->size() > right->size();
}



Generated by  Doxygen 1.6.0   Back to index