diff options
author | Enrico Zini <enrico@enricozini.org> | 2015-09-10 19:52:45 +0200 |
---|---|---|
committer | Enrico Zini <enrico@enricozini.org> | 2015-09-10 19:52:45 +0200 |
commit | c5dd516802af50f8e369e9f520b88dcf28dc77df (patch) | |
tree | b7df739f0cb01abf87bd6cae9cd55bb26920fc94 /ept | |
parent | 74e4ebe74b307d65d9064357cbf80855afbe059b (diff) | |
parent | ea85d58bc9d619e1511b40a16a630c5da9e60a42 (diff) | |
download | libept-c5dd516802af50f8e369e9f520b88dcf28dc77df.tar.gz |
Merge branch 'notagcoll': removed dependencies on tagcoll and wibble.
Diffstat (limited to 'ept')
58 files changed, 5730 insertions, 5557 deletions
diff --git a/ept/CMakeLists.txt b/ept/CMakeLists.txt index 96e1746..079cd95 100644 --- a/ept/CMakeLists.txt +++ b/ept/CMakeLists.txt @@ -1,35 +1,38 @@ project( ept ) -include( ${WIBBLE_TEST_CMAKE} ) -file( GLOB src *.cpp debtags/*.cc debtags/maint/*.cc - popcon/*.cc popcon/maint/*.cc apt/*.cc axi/*.cc ) +# Find sources and tests +file(GLOB src *.cpp debtags/*.cc debtags/maint/*.cc debtags/coll/*.cc apt/*.cc axi/*.cc utils/*.cc) +file(GLOB tests *-test.cc apt/*-test.cc debtags/*-test.cc axi/*-test.cc) +list(REMOVE_ITEM src ${tests}) +# Find headers file( GLOB h_top *.h ) file( GLOB h_apt apt/*.h ) -file( GLOB h_debtags debtags/*.h debtags/*.tcc ) -file( GLOB h_debtags_maint debtags/maint/*.h debtags/maint/*.tcc ) -file( GLOB h_popcon popcon/*.h ) -file( GLOB h_popcon_maint popcon/maint/*.h ) +file( GLOB h_debtags debtags/*.h ) +file( GLOB h_debtags_maint debtags/maint/*.h ) +file( GLOB h_debtags_coll debtags/coll/*.h ) file( GLOB h_axi axi/*.h ) +file( GLOB h_utils utils/*.h ) -file( GLOB debtagstesth debtags/*.test.h debtags/maint/*.test.h ) -file( GLOB popcontesth popcon/*.test.h ) -file( GLOB apttesth apt/*.test.h ) -file( GLOB axitesth axi/*.test.h ) -set( testh ${debtagstesth} ${popcontesth} - ${apttesth} ${axitesth} ) +#file( GLOB debtagstesth debtags/*.test.h debtags/maint/*.test.h ) +#file( GLOB apttesth apt/*.test.h ) +#file( GLOB axitesth axi/*.test.h ) +#set( testh ${debtagstesth} ${apttesth} ${axitesth} ) include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${TAGCOLL_INCLUDE_DIRS} ${WIBBLE_INCLUDE_DIRS} ) link_libraries( ${WIBBLE_LIBRARIES} ${TAGCOLL_LIBRARIES} apt-pkg xapian -lpthread ) -add_definitions( -fexceptions -fPIC -fvisibility=default ) +add_definitions( --std=c++11 -fexceptions -fPIC -fvisibility=default ) add_library( ept SHARED ${src} ) add_library( ept-static STATIC ${src} ) set_target_properties( ept PROPERTIES SOVERSION ${LIBEPT_SOVERSION} CLEAN_DIRECT_OUTPUT 1) set_target_properties( ept-static PROPERTIES SOVERSION ${LIBEPT_SOVERSION} OUTPUT_NAME "ept" CLEAN_DIRECT_OUTPUT 1) -set( TEST_ENV_DIR ${ept_BINARY_DIR}/test-env/ ) +add_executable(test-ept EXCLUDE_FROM_ALL utils/tests-main.cc ${tests}) +target_link_libraries(test-ept ept) +add_test(test-ept test-ept) +add_dependencies(check test-ept) configure_file( ${ept_SOURCE_DIR}/config.h.cmake-in ${ept_BINARY_DIR}/config.h ) @@ -44,36 +47,6 @@ configure_file( ${ept_SOURCE_DIR}/libept.pc.in # regression testing link_directories( ${CMAKE_CURRENT_BINARY_DIR}/../lib ) link_libraries( ept ) -wibble_add_test( ept-test ${testh} ) - -set( datastamp ${CMAKE_CURRENT_BINARY_DIR}/data-stamp ) -set( datadir ${CMAKE_CURRENT_SOURCE_DIR}/test-data ) -set( listfile wherever_debian_._Packages ) - -wibble_check_target( ept-test ${datastamp} ) - -execute_process( COMMAND dpkg --print-architecture OUTPUT_VARIABLE ARCH ) -string( REPLACE "\n" "" ARCH "${ARCH}" ) - -add_custom_command( - OUTPUT ${datastamp} - COMMAND mkdir -p test-env/etc test-env/state/lists/partial - test-env/cache test-env/debtags - test-env/cache/archives/partial test-env/desktop - COMMAND sed -e s,i386,${ARCH}, < ${datadir}/packagelist > test-env/state/lists/${listfile} - COMMAND cp -a ${datadir}/etc/sources.list test-env/etc/ - COMMAND sed -e s,i386,${ARCH}, < ${datadir}/dpkg-status > test-env/dpkg-status - COMMAND cp -a ${datadir}/desktop/*.desktop test-env/desktop/ - COMMAND cp ${datadir}/debtags/package-tags test-env/debtags/package-tags - COMMAND cp ${datadir}/debtags/vocabulary test-env/debtags/vocabulary - COMMAND mkdir -p test-env/debtags/empty - COMMAND mkdir -p test-env/debtags/user - COMMAND mkdir -p test-env/xapian/ - COMMAND mkdir -p test-env/popcon/ - COMMAND mkdir -p test-env/popcon/empty - COMMAND cp -a ${datadir}/popcon/all-popcon-results.txt.gz test-env/popcon/ - COMMAND cp -a ${datadir}/popcon/popularity-contest test-env/popcon/ - COMMAND touch data-stamp ) install( TARGETS ept ept-static LIBRARY DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE} @@ -84,6 +57,6 @@ install( FILES ${h_top} DESTINATION include/ept ) install( FILES ${h_apt} DESTINATION include/ept/apt ) install( FILES ${h_debtags} DESTINATION include/ept/debtags ) install( FILES ${h_debtags_maint} DESTINATION include/ept/debtags/maint ) -install( FILES ${h_popcon} DESTINATION include/ept/popcon ) -install( FILES ${h_popcon_maint} DESTINATION include/ept/popcon/maint ) +install( FILES ${h_debtags_coll} DESTINATION include/ept/debtags/coll ) install( FILES ${h_axi} DESTINATION include/ept/axi ) +install( FILES ${h_utils} DESTINATION include/ept/utils ) diff --git a/ept/apt/apt-test.cc b/ept/apt/apt-test.cc new file mode 100644 index 0000000..1a34f50 --- /dev/null +++ b/ept/apt/apt-test.cc @@ -0,0 +1,209 @@ +#include "ept/test.h" +#include "apt.h" +#include <set> +#include <algorithm> + +using namespace std; +using namespace ept; +using namespace ept::tests; +using namespace ept::apt; + +namespace { + +struct AptTestEnvironment { + //ept::core::AptDatabase db; + AptTestEnvironment() { + pkgInitConfig (*_config); + _config->Set("Initialized", 1); + _config->Set("Dir", "."); + _config->Set("Dir::Cache", "cache"); + _config->Set("Dir::State", "state"); + _config->Set("Dir::Etc", "etc"); + _config->Set("Dir::Etc::sourcelist", "sources.list"); + _config->Set("Dir::State::status", "./dpkg-status"); + pkgInitSystem (*_config, _system); + } +}; + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("iterators", []() { + // Check that iterations iterates among some packages + AptTestEnvironment env; + Apt apt; + Apt::iterator i = apt.begin(); + wassert_true(i != apt.end()); + + size_t count = 0; + for (; i != apt.end(); ++i) + ++count; + + wassert_true(count > 100); + }); + + add_method("apt_exists", []() { + // Check that iteration gives some well-known packages + AptTestEnvironment env; + Apt apt; + set<string> packages; + + std::copy(apt.begin(), apt.end(), inserter(packages, packages.begin())); + + wassert_true(packages.find("libsp1") != packages.end()); + // TODO this exposes a bug somewhere... sp definitely is among + // the packages + // wassert_true(packages.find("sp") != packages.end()); + wassert_true(packages.find("") == packages.end()); + }); + + add_method("timestamp", []() { + // Check that timestamp gives some meaningful timestamp + AptTestEnvironment env; + Apt apt; + time_t ts = apt.timestamp(); + wassert_true(ts > 1000000); + }); + + add_method("validity", []() { + // Check the package validator + AptTestEnvironment env; + Apt apt; + wassert_true(apt.isValid("apt")); + wassert_true(!apt.isValid("this-package-does-not-really-exists")); + }); + + add_method("versions", []() { + // Check the version instantiators + AptTestEnvironment env; + Apt apt; + std::string pkg("apt"); + Version ver = apt.candidateVersion(pkg); + wassert_true(ver.isValid()); + + ver = apt.installedVersion(pkg); + wassert_true(ver.isValid()); + + ver = apt.anyVersion(pkg); + wassert_true(ver.isValid()); + + std::string pkg1("this-package-does-not-really-exists"); + ver = apt.candidateVersion(pkg1); + wassert_true(!ver.isValid()); + + ver = apt.installedVersion(pkg1); + wassert_true(!ver.isValid()); + + ver = apt.anyVersion(pkg1); + wassert_true(!ver.isValid()); + }); + + add_method("version_validity", []() { + // Check the version validator + AptTestEnvironment env; + Apt apt; + Version ver = apt.candidateVersion("apt"); + wassert_true(apt.validate(ver) == ver); + + ver = Version("this-package-does-not-really-exists", "0.1"); + wassert_true(!apt.validate(ver).isValid()); + + ver = Version("apt", "0.31415"); + wassert_true(!apt.validate(ver).isValid()); + }); + + add_method("raw_record", []() { + // Check the raw record accessor + AptTestEnvironment env; + Apt apt; + string pkg("sp"); + Version ver = apt.candidateVersion(pkg); + wassert_true(ver.isValid()); + wassert_true(apt.validate(ver) == ver); + + string record = apt.rawRecord(ver); + wassert_true(record.find("Package: sp") != string::npos); + wassert_true(record.find("Section: text") != string::npos); + + record = apt.rawRecord(Version("sp", "0.31415")); + wassert(actual(record) == string()); + + wassert(actual(apt.rawRecord(pkg)) == apt.rawRecord(apt.anyVersion(pkg))); + }); + + add_method("state", []() { + // Check the package state accessor + AptTestEnvironment env; + Apt apt; + PackageState s = apt.state("kdenetwork"); + wassert_true(s.isValid()); + wassert_true(s.isInstalled()); + + s = apt.state("this-package-does-not-really-exists"); + wassert_true(!s.isValid()); + }); + + add_method("record_iteration", []() { + // Check the record iterator (accessing with *) + AptTestEnvironment env; + Apt apt; + size_t count = 0; + for (Apt::record_iterator i = apt.recordBegin(); + i != apt.recordEnd(); ++i) + { + wassert_true((*i).size() > 8); + wassert(actual((*i).substr(0, 8)) == "Package:"); + ++count; + } + wassert_true(count > 200); + }); + + add_method("record_iteration2", []() { + // Check the record iterator (accessing with ->) + AptTestEnvironment env; + Apt apt; + size_t count = 0; + for (Apt::record_iterator i = apt.recordBegin(); + i != apt.recordEnd(); ++i) + { + wassert_true(i->size() > 8); + wassert(actual(i->substr(0, 8)) == "Package:"); + ++count; + } + wassert_true(count > 200); + }); + + add_method("stl_iteration", []() { + // Check that the iterators can be used with the algorithms + AptTestEnvironment env; + Apt apt; + vector<string> out; + std::copy(apt.begin(), apt.end(), back_inserter(out)); + }); + + add_method("stl_record_iteration", []() { + // Check that the iterators can be used with the algorithms + AptTestEnvironment env; + Apt apt; + vector<string> out; + std::copy(apt.recordBegin(), apt.recordEnd(), back_inserter(out)); + }); + + add_method("check_updates", []() { + // Check that checkUpdates will keep a working Apt object + AptTestEnvironment env; + Apt apt; + wassert_true(apt.isValid("apt")); + apt.checkCacheUpdates(); + wassert_true(apt.isValid("apt")); + apt.invalidateTimestamp(); + apt.checkCacheUpdates(); + wassert_true(apt.isValid("apt")); + }); + } +} tests("apt_apt"); + +} diff --git a/ept/apt/apt.cc b/ept/apt/apt.cc index f3552f9..0825828 100644 --- a/ept/apt/apt.cc +++ b/ept/apt/apt.cc @@ -20,8 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <ept/apt/apt.h> - +#include "apt.h" +#include "ept/utils/sys.h" #include <apt-pkg/error.h> #include <apt-pkg/init.h> #include <apt-pkg/progress.h> @@ -29,12 +29,8 @@ #include <apt-pkg/pkgcachegen.h> #include <apt-pkg/policy.h> #include <apt-pkg/cachefile.h> - -#include <wibble/sys/fs.h> - #include <vector> #include <algorithm> - #include <iostream> using namespace std; @@ -44,29 +40,36 @@ namespace apt { static time_t aptTimestamp() { - namespace wfs = wibble::sys::fs; + time_t t1 = sys::timestamp(_config->FindFile("Dir::Cache::pkgcache"), 0); + time_t t2 = sys::timestamp(_config->FindFile("Dir::State::status"), 0); - time_t t1 = wfs::timestamp(_config->FindFile("Dir::Cache::pkgcache"), 0); - time_t t2 = wfs::timestamp(_config->FindFile("Dir::State::status"), 0); - return t1 > t2 ? t1 : t2; } -Exception::Exception(const std::string& context) throw () - : Generic(context) +static std::string apt_annotate_error_message(const std::string& message) { - // Concatenate all errors and warnings found - string err; - while (!_error->empty()) - { - bool type = _error->PopMessage(err); - if (type) - m_message += "E: " + err + "\n"; - else - m_message += "W: " + err + "\n"; - } + string res = message; + res += ":\n"; + // Concatenate all errors and warnings found + string err; + while (!_error->empty()) + { + bool type = _error->PopMessage(err); + if (type) + res += "E: " + err + "\n"; + else + res += "W: " + err + "\n"; + } + return res; } +Exception::Exception(const std::string& message) + : std::runtime_error(apt_annotate_error_message(message)) +{ +} + +Exception::~Exception() noexcept {} + static void aptInit () { if (_config->FindB("Initialized")) diff --git a/ept/apt/apt.h b/ept/apt/apt.h index 6e6ce74..e71ecf7 100644 --- a/ept/apt/apt.h +++ b/ept/apt/apt.h @@ -24,27 +24,20 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <wibble/exception.h> #include <ept/apt/version.h> - #include <iterator> +#include <stdexcept> class pkgCache; namespace ept { namespace apt { -class Exception : public wibble::exception::Generic +class Exception : public std::runtime_error { -protected: - std::string m_message; - public: - Exception(const std::string& context) throw (); - ~Exception() throw () {} - - virtual const char* type() const throw () { return "Apt"; } - virtual std::string desc() const throw () { return m_message; } + Exception(const std::string& message); + ~Exception() noexcept override; }; class Apt; diff --git a/ept/apt/apt.test.h b/ept/apt/apt.test.h deleted file mode 100644 index ff4f315..0000000 --- a/ept/apt/apt.test.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2007 Enrico Zini <enrico@enricozini.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <ept/test.h> -#include <ept/apt/apt.h> -#include <set> -#include <algorithm> - -using namespace std; -using namespace ept; -using namespace ept::apt; - -struct TestApt : AptTestEnvironment { - Apt apt; - - // Check that iterations iterates among some packages - Test iterators() - { - Apt::iterator i = apt.begin(); - assert(i != apt.end()); - - size_t count = 0; - for (; i != apt.end(); ++i) - ++count; - - assert(count > 100); - } - - // Check that iteration gives some well-known packages - Test aptExists() - { - set<string> packages; - - std::copy(apt.begin(), apt.end(), inserter(packages, packages.begin())); - - assert(packages.find("libsp1") != packages.end()); - // TODO this exposes a bug somewhere... sp definitely is among - // the packages - // assert(packages.find("sp") != packages.end()); - assert(packages.find("") == packages.end()); - } - - // Check that timestamp gives some meaningful timestamp - Test timestamp() - { - time_t ts = apt.timestamp(); - assert(ts > 1000000); - } - - // Check the package validator - Test validity() - { - assert(apt.isValid("apt")); - assert(!apt.isValid("this-package-does-not-really-exists")); - } - - // Check the version instantiators - Test versions() - { - std::string pkg("apt"); - Version ver = apt.candidateVersion(pkg); - assert(ver.isValid()); - - ver = apt.installedVersion(pkg); - assert(ver.isValid()); - - ver = apt.anyVersion(pkg); - assert(ver.isValid()); - - std::string pkg1("this-package-does-not-really-exists"); - ver = apt.candidateVersion(pkg1); - assert(!ver.isValid()); - - ver = apt.installedVersion(pkg1); - assert(!ver.isValid()); - - ver = apt.anyVersion(pkg1); - assert(!ver.isValid()); - } - - // Check the version validator - Test versionValidity() - { - Version ver = apt.candidateVersion("apt"); - assert(apt.validate(ver) == ver); - - ver = Version("this-package-does-not-really-exists", "0.1"); - assert(!apt.validate(ver).isValid()); - - ver = Version("apt", "0.31415"); - assert(!apt.validate(ver).isValid()); - } - - // Check the raw record accessor - Test rawRecord() - { - string pkg("sp"); - Version ver = apt.candidateVersion(pkg); - assert(ver.isValid()); - assert(apt.validate(ver) == ver); - - string record = apt.rawRecord(ver); - assert(record.find("Package: sp") != string::npos); - assert(record.find("Section: text") != string::npos); - - record = apt.rawRecord(Version("sp", "0.31415")); - assert_eq(record, string()); - - assert_eq(apt.rawRecord(pkg), apt.rawRecord(apt.anyVersion(pkg))); - } - - // Check the package state accessor - Test state() - { - PackageState s = apt.state("kdenetwork"); - assert(s.isValid()); - assert(s.isInstalled()); - - s = apt.state("this-package-does-not-really-exists"); - assert(!s.isValid()); - } - - // Check the record iterator (accessing with *) - Test recordIteration() - { - size_t count = 0; - for (Apt::record_iterator i = apt.recordBegin(); - i != apt.recordEnd(); ++i) - { - assert((*i).size() > 8); - assert_eq((*i).substr(0, 8), "Package:"); - ++count; - } - assert(count > 200); - } - - // Check the record iterator (accessing with ->) - Test recordIteration2() - { - size_t count = 0; - for (Apt::record_iterator i = apt.recordBegin(); - i != apt.recordEnd(); ++i) - { - assert(i->size() > 8); - assert_eq(i->substr(0, 8), "Package:"); - ++count; - } - assert(count > 200); - } - - // Check that the iterators can be used with the algorithms - Test stlIteration() - { - vector<string> out; - std::copy(apt.begin(), apt.end(), back_inserter(out)); - } - - // Check that the iterators can be used with the algorithms - Test stlRecordIteration() - { - vector<string> out; - std::copy(apt.recordBegin(), apt.recordEnd(), back_inserter(out)); - } - - // Check that checkUpdates will keep a working Apt object - Test checkUpdates() - { - assert(apt.isValid("apt")); - apt.checkCacheUpdates(); - assert(apt.isValid("apt")); - apt.invalidateTimestamp(); - apt.checkCacheUpdates(); - assert(apt.isValid("apt")); - } - -}; - -// vim:set ts=4 sw=4: diff --git a/ept/apt/packagerecord-test.cc b/ept/apt/packagerecord-test.cc new file mode 100644 index 0000000..b421f5b --- /dev/null +++ b/ept/apt/packagerecord-test.cc @@ -0,0 +1,126 @@ +#include "ept/test.h" +#include "packagerecord.h" + +namespace std { + ostream& operator<<(ostream& out, const set<string>& s) + { + for (set<string>::const_iterator i = s.begin(); + i != s.end(); ++i) + if (i == s.begin()) + out << *i; + else + out << ", " << *i; + return out; + } +} + +using namespace std; +using namespace ept; +using namespace ept::tests; +using namespace ept::apt; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("supported_fields", []() { + // Check that the supported fields are understood + string record = + "Package: apt\n" + "Priority: important\n" + "Section: admin\n" + "Installed-Size: 4368\n" + "Maintainer: APT Development Team <deity@lists.debian.org>\n" + "Architecture: amd64\n" + "Source: apt\n" + "Version: 0.6.46.4-0.1\n" + "Replaces: libapt-pkg-doc (<< 0.3.7), libapt-pkg-dev (<< 0.3.7)\n" + "Provides: libapt-pkg-libc6.3-6-3.11\n" + "Depends: libc6 (>= 2.3.5-1), libgcc1 (>= 1:4.1.1-12), libstdc++6 (>= 4.1.1-12), debian-archive-keyring\n" + "Pre-Depends: debtags (maybe)\n" + "Suggests: aptitude | synaptic | gnome-apt | wajig, dpkg-dev, apt-doc, bzip2\n" + "Recommends: debtags (maybe)\n" + "Enhances: debian\n" + "Conflicts: marameo\n" + "Filename: pool/main/a/apt/apt_0.6.46.4-0.1_amd64.deb\n" + "Size: 1436478\n" + "MD5sum: 1776421f80d6300c77a608e77a9f4a15\n" + "SHA1: 1bd7337d2df56d267632cf72ac930c0a4895898f\n" + "SHA256: b92442ab60046b4d0728245f39cc932f26e17db9f7933a5ec9aaa63172f51fda\n" + "Description: Advanced front-end for dpkg\n" + " This is Debian's next generation front-end for the dpkg package manager.\n" + " It provides the apt-get utility and APT dselect method that provides a\n" + " simpler, safer way to install and upgrade packages.\n" + " .\n" + " APT features complete installation ordering, multiple source capability\n" + " and several other unique features, see the Users Guide in apt-doc.\n" + "Build-Essential: yes\n" + "Tag: admin::package-management, filetransfer::ftp, filetransfer::http, hardware::storage:cd, interface::commandline, network::client, protocol::{ftp,http,ipv6}, role::program, suite::debian, use::downloading, use::searching, works-with::software:package\n"; + + PackageRecord p(record); + + wassert(actual(p.size()) == 24u); + + wassert(actual(p.package()) == "apt"); + wassert(actual(p.priority()) == "important"); + wassert(actual(p.section()) == "admin"); + wassert(actual(p.installedSize()) == 4368u); + wassert(actual(p.maintainer()) == "APT Development Team <deity@lists.debian.org>"); + wassert(actual(p.architecture()) == "amd64"); + wassert(actual(p.source()) == "apt"); + wassert(actual(p.version()) == "0.6.46.4-0.1"); + wassert(actual(p.replaces()) == "libapt-pkg-doc (<< 0.3.7), libapt-pkg-dev (<< 0.3.7)"); + wassert(actual(p.provides()) == "libapt-pkg-libc6.3-6-3.11"); + wassert(actual(p.depends()) == "libc6 (>= 2.3.5-1), libgcc1 (>= 1:4.1.1-12), libstdc++6 (>= 4.1.1-12), debian-archive-keyring"); + wassert(actual(p.preDepends()) == "debtags (maybe)"); + wassert(actual(p.recommends()) == "debtags (maybe)"); + wassert(actual(p.suggests()) == "aptitude | synaptic | gnome-apt | wajig, dpkg-dev, apt-doc, bzip2"); + wassert(actual(p.enhances()) == "debian"); + wassert(actual(p.conflicts()) == "marameo"); + wassert(actual(p.filename()) == "pool/main/a/apt/apt_0.6.46.4-0.1_amd64.deb"); + wassert(actual(p.packageSize()) == 1436478u); + wassert(actual(p.md5sum()) == "1776421f80d6300c77a608e77a9f4a15"); + wassert(actual(p.sha1()) == "1bd7337d2df56d267632cf72ac930c0a4895898f"); + wassert(actual(p.sha256()) == "b92442ab60046b4d0728245f39cc932f26e17db9f7933a5ec9aaa63172f51fda"); + wassert(actual(p.description()) == "Advanced front-end for dpkg\n" + " This is Debian's next generation front-end for the dpkg package manager.\n" + " It provides the apt-get utility and APT dselect method that provides a\n" + " simpler, safer way to install and upgrade packages.\n" + " .\n" + " APT features complete installation ordering, multiple source capability\n" + " and several other unique features, see the Users Guide in apt-doc."); + wassert(actual(p.shortDescription()) == "Advanced front-end for dpkg"); + wassert(actual(p.longDescription()) == + "This is Debian's next generation front-end for the dpkg package manager.\n" + " It provides the apt-get utility and APT dselect method that provides a\n" + " simpler, safer way to install and upgrade packages.\n" + " .\n" + " APT features complete installation ordering, multiple source capability\n" + " and several other unique features, see the Users Guide in apt-doc."); + wassert(actual(p.buildEssential()) == true); + + std::set<std::string> tags; + tags.insert("admin::package-management"); + tags.insert("filetransfer::ftp"); + tags.insert("filetransfer::http"); + tags.insert("hardware::storage:cd"); + tags.insert("interface::commandline"); + tags.insert("network::client"); + tags.insert("protocol::ftp"); + tags.insert("protocol::http"); + tags.insert("protocol::ipv6"); + tags.insert("role::program"); + tags.insert("suite::debian"); + tags.insert("use::downloading"); + tags.insert("use::searching"); + tags.insert("works-with::software:package"); + wassert(actual(p.tag()) == tags); + }); + } +} tests("apt_packagerecord"); + +} diff --git a/ept/apt/packagerecord.test.h b/ept/apt/packagerecord.test.h deleted file mode 100644 index 657cbab..0000000 --- a/ept/apt/packagerecord.test.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2007 Enrico Zini <enrico@enricozini.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <ept/test.h> -#include <ept/apt/packagerecord.h> - -namespace std { - ostream& operator<<(ostream& out, const set<string>& s) - { - for (set<string>::const_iterator i = s.begin(); - i != s.end(); ++i) - if (i == s.begin()) - out << *i; - else - out << ", " << *i; - return out; - } -} - -using namespace std; -using namespace ept; -using namespace ept::apt; - -struct TestAptPackagerecord { - - // Check that the supported fields are understood - Test supportedFields() - { - string record = - "Package: apt\n" - "Priority: important\n" - "Section: admin\n" - "Installed-Size: 4368\n" - "Maintainer: APT Development Team <deity@lists.debian.org>\n" - "Architecture: amd64\n" - "Source: apt\n" - "Version: 0.6.46.4-0.1\n" - "Replaces: libapt-pkg-doc (<< 0.3.7), libapt-pkg-dev (<< 0.3.7)\n" - "Provides: libapt-pkg-libc6.3-6-3.11\n" - "Depends: libc6 (>= 2.3.5-1), libgcc1 (>= 1:4.1.1-12), libstdc++6 (>= 4.1.1-12), debian-archive-keyring\n" - "Pre-Depends: debtags (maybe)\n" - "Suggests: aptitude | synaptic | gnome-apt | wajig, dpkg-dev, apt-doc, bzip2\n" - "Recommends: debtags (maybe)\n" - "Enhances: debian\n" - "Conflicts: marameo\n" - "Filename: pool/main/a/apt/apt_0.6.46.4-0.1_amd64.deb\n" - "Size: 1436478\n" - "MD5sum: 1776421f80d6300c77a608e77a9f4a15\n" - "SHA1: 1bd7337d2df56d267632cf72ac930c0a4895898f\n" - "SHA256: b92442ab60046b4d0728245f39cc932f26e17db9f7933a5ec9aaa63172f51fda\n" - "Description: Advanced front-end for dpkg\n" - " This is Debian's next generation front-end for the dpkg package manager.\n" - " It provides the apt-get utility and APT dselect method that provides a\n" - " simpler, safer way to install and upgrade packages.\n" - " .\n" - " APT features complete installation ordering, multiple source capability\n" - " and several other unique features, see the Users Guide in apt-doc.\n" - "Build-Essential: yes\n" - "Tag: admin::package-management, filetransfer::ftp, filetransfer::http, hardware::storage:cd, interface::commandline, network::client, protocol::{ftp,http,ipv6}, role::program, suite::debian, use::downloading, use::searching, works-with::software:package\n"; - - PackageRecord p(record); - - assert_eq(p.size(), 24u); - - assert_eq(p.package(), "apt"); - assert_eq(p.priority(), "important"); - assert_eq(p.section(), "admin"); - assert_eq(p.installedSize(), 4368u); - assert_eq(p.maintainer(), "APT Development Team <deity@lists.debian.org>"); - assert_eq(p.architecture(), "amd64"); - assert_eq(p.source(), "apt"); - assert_eq(p.version(), "0.6.46.4-0.1"); - assert_eq(p.replaces(), "libapt-pkg-doc (<< 0.3.7), libapt-pkg-dev (<< 0.3.7)"); - assert_eq(p.provides(), "libapt-pkg-libc6.3-6-3.11"); - assert_eq(p.depends(), "libc6 (>= 2.3.5-1), libgcc1 (>= 1:4.1.1-12), libstdc++6 (>= 4.1.1-12), debian-archive-keyring"); - assert_eq(p.preDepends(), "debtags (maybe)"); - assert_eq(p.recommends(), "debtags (maybe)"); - assert_eq(p.suggests(), "aptitude | synaptic | gnome-apt | wajig, dpkg-dev, apt-doc, bzip2"); - assert_eq(p.enhances(), "debian"); - assert_eq(p.conflicts(), "marameo"); - assert_eq(p.filename(), "pool/main/a/apt/apt_0.6.46.4-0.1_amd64.deb"); - assert_eq(p.packageSize(), 1436478u); - assert_eq(p.md5sum(), "1776421f80d6300c77a608e77a9f4a15"); - assert_eq(p.sha1(), "1bd7337d2df56d267632cf72ac930c0a4895898f"); - assert_eq(p.sha256(), "b92442ab60046b4d0728245f39cc932f26e17db9f7933a5ec9aaa63172f51fda"); - assert_eq(p.description(), "Advanced front-end for dpkg\n" - " This is Debian's next generation front-end for the dpkg package manager.\n" - " It provides the apt-get utility and APT dselect method that provides a\n" - " simpler, safer way to install and upgrade packages.\n" - " .\n" - " APT features complete installation ordering, multiple source capability\n" - " and several other unique features, see the Users Guide in apt-doc."); - assert_eq(p.shortDescription(), "Advanced front-end for dpkg"); - assert_eq(p.longDescription(), - "This is Debian's next generation front-end for the dpkg package manager.\n" - " It provides the apt-get utility and APT dselect method that provides a\n" - " simpler, safer way to install and upgrade packages.\n" - " .\n" - " APT features complete installation ordering, multiple source capability\n" - " and several other unique features, see the Users Guide in apt-doc."); - assert_eq(p.buildEssential(), true); - - std::set<std::string> tags; - tags.insert("admin::package-management"); - tags.insert("filetransfer::ftp"); - tags.insert("filetransfer::http"); - tags.insert("hardware::storage:cd"); - tags.insert("interface::commandline"); - tags.insert("network::client"); - tags.insert("protocol::ftp"); - tags.insert("protocol::http"); - tags.insert("protocol::ipv6"); - tags.insert("role::program"); - tags.insert("suite::debian"); - tags.insert("use::downloading"); - tags.insert("use::searching"); - tags.insert("works-with::software:package"); - assert_eq(p.tag(), tags); - } - -}; - -// vim:set ts=4 sw=4: diff --git a/ept/apt/recordparser-test.cc b/ept/apt/recordparser-test.cc new file mode 100644 index 0000000..4e01e6b --- /dev/null +++ b/ept/apt/recordparser-test.cc @@ -0,0 +1,200 @@ +#include "ept/test.h" +#include "recordparser.h" + +using namespace ept::tests; +using namespace std; +using namespace ept; +using namespace ept::apt; + +namespace { + +const char* test_record = + "A:\n" + "D: da de di do du\n" + "B: b\n" + "C: c \n" + "Desc: this is the beginning\n" + " this is the continuation\n" + " this is the end\n"; + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("parsing", []() { + // Check that the fields are identified and broken up correctly + RecordParser p(test_record); + + wassert(actual(p.record()) == test_record); + wassert(actual(p.size()) == 5u); + }); + + add_method("field_tuples", []() { + RecordParser p(test_record); + wassert(actual(p.field(0)) == "A:\n"); + wassert(actual(p.field(1)) == "D: da de di do du\n"); + wassert(actual(p.field(2)) == "B: b\n"); + wassert(actual(p.field(3)) == "C: c \n"); + wassert(actual(p.field(4)) == "Desc: this is the beginning\n this is the continuation\n this is the end\n"); + }); + + add_method("field_keys", []() { + RecordParser p(test_record); + wassert(actual(p.name(0)) == "A"); + wassert(actual(p.name(1)) == "D"); + wassert(actual(p.name(2)) == "B"); + wassert(actual(p.name(3)) == "C"); + wassert(actual(p.name(4)) == "Desc"); + }); + + add_method("field_values", []() { + RecordParser p(test_record); + wassert(actual(p[0]) == ""); + wassert(actual(p[1]) == "da de di do du"); + wassert(actual(p[2]) == "b"); + wassert(actual(p[3]) == "c"); + wassert(actual(p[4]) == "this is the beginning\n this is the continuation\n this is the end"); + }); + + add_method("find_byname", []() { + // Check that the field search by name finds all the fields + RecordParser p(test_record); + + wassert(actual(p.index("A")) == 0u); + wassert(actual(p.index("D")) == 1u); + wassert(actual(p.index("B")) == 2u); + wassert(actual(p.index("C")) == 3u); + wassert(actual(p.index("Desc")) == 4u); + + wassert(actual(p.name(p.index("A"))) == "A"); + wassert(actual(p.name(p.index("B"))) == "B"); + wassert(actual(p.name(p.index("C"))) == "C"); + wassert(actual(p.name(p.index("D"))) == "D"); + wassert(actual(p.name(p.index("Desc"))) == "Desc"); + }); + + add_method("indexing", []() { + RecordParser p(test_record); + wassert(actual(p["A"]) == ""); + wassert(actual(p["B"]) == "b"); + wassert(actual(p["C"]) == "c"); + wassert(actual(p["D"]) == "da de di do du"); + wassert(actual(p["Desc"]) == "this is the beginning\n this is the continuation\n this is the end"); + }); + + add_method("missing_behaviour", []() { + RecordParser p(test_record); + // Missing fields give empty strings + wassert(actual(p.field(100)) == ""); + wassert(actual(p.name(100)) == ""); + wassert(actual(p[100]) == ""); + wassert(actual(p["Missing"]) == ""); + }); + + add_method("rescan", []() { + // Check that scanning twice replaces the old fields + std::string record = + "A: a\n" + "B: b\n" + "C: c\n"; + + RecordParser p(record); + wassert(actual(p.size()) == 3u); + wassert(actual(p["A"]) == "a"); + wassert(actual(p["B"]) == "b"); + wassert(actual(p["C"]) == "c"); + + std::string record1 = + "Foo: bar\n" + "A: different\n"; + + p.scan(record1); + + //for (size_t i = 0; i < p.size(); ++i) + // cerr << ">> " << i << "==" << p.index(p.name(i)) << " " << p.name(i) << " " << p[i] << endl; + + wassert(actual(p.size()) == 2u); + wassert(actual(p["A"]) == "different"); + wassert(actual(p["B"]) == ""); + wassert(actual(p["C"]) == ""); + wassert(actual(p["Foo"]) == "bar"); + }); + + add_method("real_life", []() { + // Real-life example + string record = + "Package: apt\n" + "Priority: important\n" + "Section: admin\n" + "Installed-Size: 4368\n" + "Maintainer: APT Development Team <deity@lists.debian.org>\n" + "Architecture: amd64\n" + "Version: 0.6.46.4-0.1\n" + "Replaces: libapt-pkg-doc (<< 0.3.7), libapt-pkg-dev (<< 0.3.7)\n" + "Provides: libapt-pkg-libc6.3-6-3.11\n" + "Depends: libc6 (>= 2.3.5-1), libgcc1 (>= 1:4.1.1-12), libstdc++6 (>= 4.1.1-12), debian-archive-keyring\n" + "Suggests: aptitude | synaptic | gnome-apt | wajig, dpkg-dev, apt-doc, bzip2\n" + "Filename: pool/main/a/apt/apt_0.6.46.4-0.1_amd64.deb\n" + "Size: 1436478\n" + "MD5sum: 1776421f80d6300c77a608e77a9f4a15\n" + "SHA1: 1bd7337d2df56d267632cf72ac930c0a4895898f\n" + "SHA256: b92442ab60046b4d0728245f39cc932f26e17db9f7933a5ec9aaa63172f51fda\n" + "Description: Advanced front-end for dpkg\n" + " This is Debian's next generation front-end for the dpkg package manager.\n" + " It provides the apt-get utility and APT dselect method that provides a\n" + " simpler, safer way to install and upgrade packages.\n" + " .\n" + " APT features complete installation ordering, multiple source capability\n" + " and several other unique features, see the Users Guide in apt-doc.\n" + "Build-Essential: yes\n" + "Tag: admin::package-management, filetransfer::ftp, filetransfer::http, hardware::storage:cd, interface::commandline, network::client, protocol::{ftp,http,ipv6}, role::program, suite::debian, use::downloading, use::searching, works-with::software:package\n"; + RecordParser p(record); + + wassert(actual(p.size()) == 19u); + + string rec1; + for (size_t i = 0; i < p.size(); ++i) + rec1 += p.field(i); + wassert(actual(record) == rec1); + }); + + add_method("buffer_termination", []() { + // Various buffer termination patterns + std::string record = + "A: a\n" + "B: b"; + + RecordParser p(record); + wassert(actual(p.size()) == 2u); + wassert(actual(p["A"]) == "a"); + wassert(actual(p["B"]) == "b"); + }); + + add_method("buffer_termination2", []() { + std::string record = + "A: a\n" + "B: b\n\n"; + + RecordParser p(record); + wassert(actual(p.size()) == 2u); + wassert(actual(p["A"]) == "a"); + wassert(actual(p["B"]) == "b"); + }); + + add_method("buffer_termination3", []() { + std::string record = + "A: a\n" + "B: b\n\n" + "C: c\n"; + + RecordParser p(record); + wassert(actual(p.size()) == 2u); + wassert(actual(p["A"]) == "a"); + wassert(actual(p["B"]) == "b"); + }); + } +} tests("apt_recordparser"); + +} diff --git a/ept/apt/recordparser.test.h b/ept/apt/recordparser.test.h deleted file mode 100644 index 629008f..0000000 --- a/ept/apt/recordparser.test.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2007 Enrico Zini <enrico@enricozini.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <ept/test.h> -#include <ept/apt/recordparser.h> - -//#include <iostream> - -using namespace std; -using namespace ept; -using namespace ept::apt; - -struct TestAptRecordparser { - std::string record; - TestAptRecordparser() - { - record = - "A:\n" - "D: da de di do du\n" - "B: b\n" - "C: c \n" - "Desc: this is the beginning\n" - " this is the continuation\n" - " this is the end\n"; - } - - // Check that the fields are identified and broken up correctly - Test parsing() - { - RecordParser p(record); - - assert_eq(p.record(), record); - assert_eq(p.size(), 5u); - } - - Test fieldTuples() - { - RecordParser p(record); - assert_eq(p.field(0), "A:\n"); - assert_eq(p.field(1), "D: da de di do du\n"); - assert_eq(p.field(2), "B: b\n"); - assert_eq(p.field(3), "C: c \n"); - assert_eq(p.field(4), "Desc: this is the beginning\n this is the continuation\n this is the end\n"); - } - - Test fieldKeys() - { - RecordParser p(record); - assert_eq(p.name(0), "A"); - assert_eq(p.name(1), "D"); - assert_eq(p.name(2), "B"); - assert_eq(p.name(3), "C"); - assert_eq(p.name(4), "Desc"); - } - - Test fieldValues() - { - RecordParser p(record); - assert_eq(p[0], ""); - assert_eq(p[1], "da de di do du"); - assert_eq(p[2], "b"); - assert_eq(p[3], "c"); - assert_eq(p[4], "this is the beginning\n this is the continuation\n this is the end"); - } - - // Check that the field search by name finds all the fields - Test findByName() - { - RecordParser p(record); - - assert_eq(p.index("A"), 0u); - assert_eq(p.index("D"), 1u); - assert_eq(p.index("B"), 2u); - assert_eq(p.index("C"), 3u); - assert_eq(p.index("Desc"), 4u); - - assert_eq(p.name(p.index("A")), "A"); - assert_eq(p.name(p.index("B")), "B"); - assert_eq(p.name(p.index("C")), "C"); - assert_eq(p.name(p.index("D")), "D"); - assert_eq(p.name(p.index("Desc")), "Desc"); - } - - Test indexing() - { - RecordParser p(record); - assert_eq(p["A"], ""); - assert_eq(p["B"], "b"); - assert_eq(p["C"], "c"); - assert_eq(p["D"], "da de di do du"); - assert_eq(p["Desc"], "this is the beginning\n this is the continuation\n this is the end"); - } - - Test missingBehaviour() - { - RecordParser p(record); - // Missing fields give empty strings - assert_eq(p.field(100), ""); - assert_eq(p.name(100), ""); - assert_eq(p[100], ""); - assert_eq(p["Missing"], ""); - } - - // Check that scanning twice replaces the old fields - Test rescan() - { - std::string record = - "A: a\n" - "B: b\n" - "C: c\n"; - - RecordParser p(record); - assert_eq(p.size(), 3u); - assert_eq(p["A"], "a"); - assert_eq(p["B"], "b"); - assert_eq(p["C"], "c"); - - std::string record1 = - "Foo: bar\n" - "A: different\n"; - - p.scan(record1); - - //for (size_t i = 0; i < p.size(); ++i) - // cerr << ">> " << i << "==" << p.index(p.name(i)) << " " << p.name(i) << " " << p[i] << endl; - - assert_eq(p.size(), 2u); - assert_eq(p["A"], "different"); - assert_eq(p["B"], ""); - assert_eq(p["C"], ""); - assert_eq(p["Foo"], "bar"); - } - - // Real-life example - Test realLife() - { - string record = - "Package: apt\n" - "Priority: important\n" - "Section: admin\n" - "Installed-Size: 4368\n" - "Maintainer: APT Development Team <deity@lists.debian.org>\n" - "Architecture: amd64\n" - "Version: 0.6.46.4-0.1\n" - "Replaces: libapt-pkg-doc (<< 0.3.7), libapt-pkg-dev (<< 0.3.7)\n" - "Provides: libapt-pkg-libc6.3-6-3.11\n" - "Depends: libc6 (>= 2.3.5-1), libgcc1 (>= 1:4.1.1-12), libstdc++6 (>= 4.1.1-12), debian-archive-keyring\n" - "Suggests: aptitude | synaptic | gnome-apt | wajig, dpkg-dev, apt-doc, bzip2\n" - "Filename: pool/main/a/apt/apt_0.6.46.4-0.1_amd64.deb\n" - "Size: 1436478\n" - "MD5sum: 1776421f80d6300c77a608e77a9f4a15\n" - "SHA1: 1bd7337d2df56d267632cf72ac930c0a4895898f\n" - "SHA256: b92442ab60046b4d0728245f39cc932f26e17db9f7933a5ec9aaa63172f51fda\n" - "Description: Advanced front-end for dpkg\n" - " This is Debian's next generation front-end for the dpkg package manager.\n" - " It provides the apt-get utility and APT dselect method that provides a\n" - " simpler, safer way to install and upgrade packages.\n" - " .\n" - " APT features complete installation ordering, multiple source capability\n" - " and several other unique features, see the Users Guide in apt-doc.\n" - "Build-Essential: yes\n" - "Tag: admin::package-management, filetransfer::ftp, filetransfer::http, hardware::storage:cd, interface::commandline, network::client, protocol::{ftp,http,ipv6}, role::program, suite::debian, use::downloading, use::searching, works-with::software:package\n"; - RecordParser p(record); - - assert_eq(p.size(), 19u); - - string rec1; - for (size_t i = 0; i < p.size(); ++i) - rec1 += p.field(i); - assert_eq(record, rec1); - } - - // Various buffer termination patterns - Test bufferTermination() - { - std::string record = - "A: a\n" - "B: b"; - - RecordParser p(record); - assert_eq(p.size(), 2u); - assert_eq(p["A"], "a"); - assert_eq(p["B"], "b"); - } - - Test bufferTermination2() - { - std::string record = - "A: a\n" - "B: b\n\n"; - - RecordParser p(record); - assert_eq(p.size(), 2u); - assert_eq(p["A"], "a"); - assert_eq(p["B"], "b"); - } - - Test bufferTermination3() - { - std::string record = - "A: a\n" - "B: b\n\n" - "C: c\n"; - - RecordParser p(record); - assert_eq(p.size(), 2u); - assert_eq(p["A"], "a"); - assert_eq(p["B"], "b"); - } - -}; - -// vim:set ts=4 sw=4: diff --git a/ept/apt/version-test.cc b/ept/apt/version-test.cc new file mode 100644 index 0000000..0fac7dc --- /dev/null +++ b/ept/apt/version-test.cc @@ -0,0 +1,119 @@ +#include "ept/test.h" +#include "version.h" + +using namespace std; +using namespace ept::tests; +using namespace ept::apt; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("invalid", []() { + // Basic test for invalid version + Version test; + + wassert(actual(test.name()) == ""); + wassert(actual(test.version()) == ""); + wassert(actual(test.isValid()) == false); + + string p = test.name(); + + wassert(actual(p) == string()); + }); + + add_method("basic", []() { + // Basic test for version + Version test("test", "1.0"); + + wassert(actual(test.name()) == "test"); + wassert(actual(test.version()) == "1.0"); + wassert(actual(test.isValid()) == true); + + string p = test.name(); + + wassert(actual(p) == "test"); + + Version v(p, "1.1"); + wassert(actual(v.name()) == "test"); + wassert(actual(v.version()) == "1.1"); + wassert(actual(v.isValid()) == true); + }); + + add_method("comparison", []() { + // Comparison semanthics + Version test("test", "1.0"); + Version test1("test", "1.0"); + + wassert_true(test == test1); + wassert_true(! (test != test1)); + wassert_true(! (test < test1)); + wassert_true(! (test > test1)); + wassert_true(test <= test1); + wassert_true(test >= test1); + + + Version test2("test2", "1.0"); + + wassert_true(test != test2); + wassert_true(test != test2); + wassert_true(test < test2); + wassert_true(! (test > test2)); + wassert_true(test <= test2); + wassert_true(! (test >= test2)); + + + Version test3("test", "2.0"); + + wassert_true(test != test3); + wassert_true(test != test3); + wassert_true(test < test3); + wassert_true(! (test > test3)); + wassert_true(test <= test3); + wassert_true(! (test >= test3)); + }); + + add_method("value_copy", []() { + // Value-copy semanthics + Version test("test", "1.0"); + Version test1 = test; + + wassert_true(test == test1); + + Version test2; + test2 = test; + wassert_true(test == test2); + wassert_true(test1 == test2); + + Version test3("test", "1.0"); + wassert_true(test == test3); + wassert_true(test1 == test3); + wassert_true(test2 == test3); + }); + + add_method("upstream_version", []() { + // Extraction of upstream version + wassert(actual(Version("a", "10.0").upstreamVersion()) == "10.0"); + wassert(actual(Version("a", "10.0-1").upstreamVersion()) == "10.0"); + wassert(actual(Version("a", "10.0~foo.1-1.0").upstreamVersion()) == "10.0~foo.1"); + wassert(actual(Version("a", "1.0:10.0~foo.1-1.0").upstreamVersion()) == "10.0~foo.1"); + }); + + add_method("policy_comparison", []() { + // Debian policy comparison semanthics + wassert_true(Version("a", "10.0") > Version("a", "2.1")); + wassert_true(Version("a", "1:10.0") < Version("a", "2:2.1")); + wassert_true(Version("a", "10.0-1") < Version("a", "10.0-2")); + wassert_true(Version("a", "10.0-2") > Version("a", "10.0-1")); + wassert_true(Version("a", "1:10.0-1") <= Version("a", "1:10.0-1")); + wassert_true(Version("a", "1:10.0-1") >= Version("a", "1:10.0-1")); + // TODO: add more + }); + } +} tests("apt_version"); + +} diff --git a/ept/apt/version.test.h b/ept/apt/version.test.h deleted file mode 100644 index a06a5c6..0000000 --- a/ept/apt/version.test.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2007 Enrico Zini <enrico@enricozini.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <ept/test.h> -#include <ept/apt/version.h> - -using namespace std; -using namespace ept::apt; - -struct TestAptVersion { - - // Basic test for invalid version - Test invalid() - { - Version test; - - assert_eq(test.name(), ""); - assert_eq(test.version(), ""); - assert_eq(test.isValid(), false); - - string p = test.name(); - - assert_eq(p, string()); - } - - // Basic test for version - Test basic() - { - Version test("test", "1.0"); - - assert_eq(test.name(), "test"); - assert_eq(test.version(), "1.0"); - assert_eq(test.isValid(), true); - - string p = test.name(); - - assert_eq(p, "test"); - - Version v(p, "1.1"); - assert_eq(v.name(), "test"); - assert_eq(v.version(), "1.1"); - assert_eq(v.isValid(), true); - } - - // Comparison semanthics - Test comparison() - { - Version test("test", "1.0"); - Version test1("test", "1.0"); - - assert(test == test1); - assert(! (test != test1)); - assert(! (test < test1)); - assert(! (test > test1)); - assert(test <= test1); - assert(test >= test1); - - - Version test2("test2", "1.0"); - - assert(test != test2); - assert(test != test2); - assert(test < test2); - assert(! (test > test2)); - assert(test <= test2); - assert(! (test >= test2)); - - - Version test3("test", "2.0"); - - assert(test != test3); - assert(test != test3); - assert(test < test3); - assert(! (test > test3)); - assert(test <= test3); - assert(! (test >= test3)); - } - - // Value-copy semanthics - Test valueCopy() - { - Version test("test", "1.0"); - Version test1 = test; - - assert(test == test1); - - Version test2; - test2 = test; - assert(test == test2); - assert(test1 == test2); - - Version test3("test", "1.0"); - assert(test == test3); - assert(test1 == test3); - assert(test2 == test3); - } - - // Extraction of upstream version - Test upstreamVersion() - { - assert_eq(Version("a", "10.0").upstreamVersion(), "10.0"); - assert_eq(Version("a", "10.0-1").upstreamVersion(), "10.0"); - assert_eq(Version("a", "10.0~foo.1-1.0").upstreamVersion(), "10.0~foo.1"); - assert_eq(Version("a", "1.0:10.0~foo.1-1.0").upstreamVersion(), "10.0~foo.1"); - } - - // Debian policy comparison semanthics - Test policyComparison() - { - assert(Version("a", "10.0") > Version("a", "2.1")); - assert(Version("a", "1:10.0") < Version("a", "2:2.1")); - assert(Version("a", "10.0-1") < Version("a", "10.0-2")); - assert(Version("a", "10.0-2") > Version("a", "10.0-1")); - assert(Version("a", "1:10.0-1") <= Version("a", "1:10.0-1")); - assert(Version("a", "1:10.0-1") >= Version("a", "1:10.0-1")); - // TODO: add more - } - -}; - -// vim:set ts=4 sw=4: diff --git a/ept/axi/axi-test.cc b/ept/axi/axi-test.cc new file mode 100644 index 0000000..3e39b19 --- /dev/null +++ b/ept/axi/axi-test.cc @@ -0,0 +1,30 @@ +#include "ept/test.h" +#include "axi.h" +#include "ept/apt/apt.h" +#include "ept/utils/sys.h" +#include <set> + +using namespace ept::tests; +using namespace std; +using namespace ept; + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("empty", []() { + // Access an empty index + sys::mkdir_ifmissing("xapian", 0755); + apt::Apt apt; + axi::OverrideIndexDir oid("./empty"); + wassert(actual(axi::timestamp()) == 0); + sys::rmdir("xapian"); + }); + } +} tests("axi"); + +} diff --git a/ept/axi/axi.cc b/ept/axi/axi.cc index 020d079..491e181 100644 --- a/ept/axi/axi.cc +++ b/ept/axi/axi.cc @@ -23,15 +23,12 @@ */ #include <ept/config.h> -#include <ept/axi/axi.h> - -#include <wibble/exception.h> -#include <wibble/string.h> -#include <wibble/sys/fs.h> +#include "axi.h" +#include "ept/utils/sys.h" +#include "ept/utils/string.h" #include <memory> using namespace std; -using namespace wibble; namespace ept { namespace axi { @@ -50,8 +47,8 @@ std::string path_db() time_t timestamp() { - string tsfile = str::joinpath(m_index_dir, "update-timestamp"); - return sys::fs::timestamp(tsfile, 0); + string tsfile = str::joinpath(m_index_dir, "update-timestamp"); + return sys::timestamp(tsfile, 0); } diff --git a/ept/axi/axi.test.h b/ept/axi/axi.test.h deleted file mode 100644 index 5481bd8..0000000 --- a/ept/axi/axi.test.h +++ /dev/null @@ -1,58 +0,0 @@ -// -*- mode: c++; tab-width: 4; indent-tabs-mode: t -*- -/* - * popcon test - * - * Copyright (C) 2007 Enrico Zini <enrico@debian.org> - * - * 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 <ept/test.h> -#include <ept/axi/axi.h> -#include <ept/apt/apt.h> -#include <wibble/sys/fs.h> -#include <set> - -using namespace std; -using namespace ept; - -struct DirMaker -{ - DirMaker(const std::string& name) - { - wibble::sys::fs::mkdirIfMissing(name, 0755); - } -}; - -struct TestAxi : AptTestEnvironment -{ - DirMaker md; - axi::OverrideIndexDir oid; - apt::Apt apt; - - TestAxi() - : md( TEST_ENV_DIR "xapian"), oid( TEST_ENV_DIR "xapian") - { - } - -// Access an empty index - Test empty() - { - axi::OverrideIndexDir oid("./empty"); - assert_eq(axi::timestamp(), 0); - } -}; - -// vim:set ts=4 sw=4: diff --git a/ept/debtags/coll/TextFormat.cc b/ept/debtags/coll/TextFormat.cc new file mode 100644 index 0000000..7681d3d --- /dev/null +++ b/ept/debtags/coll/TextFormat.cc @@ -0,0 +1,221 @@ +/* + * Serialize a tagged collection to a text file + * + * Copyright (C) 2003--2015 Enrico Zini <enrico@debian.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "TextFormat.h" +#include "fast.h" +#include "operators.h" +#include <stdexcept> +#include <system_error> +#include <set> + +using namespace std; +using namespace ept::debtags::coll::operators; + +namespace ept { +namespace debtags { +namespace coll { +namespace textformat { + +// Parse an element +// Return the trailing separating char, that can be: +// input::Input::Eof +// '\n' +// ':' +// ',' +// Return the item in `item' + +// element: \s*[^ \t,:]\s*([.:])\s* +// or +// element: \s*[^ \t,:].*?[^ \t,:]\s*([.:])\s+ +int parseElement(FILE* in, const std::string& pathname, string& item) +{ + item = string(); + string sep; + int c; + char sepchar = 0; + enum {LSPACE, ITEM, ISPACE, ISEP, TSPACE} state = LSPACE; + while ((c = getc(in)) != EOF) + { + if (c == '\n') + { + if (sepchar && sepchar != ':') + throw std::runtime_error("separator character ends the line"); + else + return '\n'; + } + switch (state) + { + // Optional leading space + case LSPACE: + switch (c) + { + case ' ': + case '\t': + break; + case ':': + case ',': + throw std::runtime_error("element cannot start with a separation character"); + break; + default: + item += c; + state = ITEM; + break; + } + break; + // Non-separating characters + case ITEM: + switch (c) + { + case ' ': + case '\t': + sep += c; + state = ISPACE; + break; + case ':': + case ',': + sepchar = c; + sep += c; + state = ISEP; + break; + default: + item += c; + break; + } + break; + // Space inside item or at the end of item + case ISPACE: + switch (c) + { + case ' ': + case '\t': + sep += c; + break; + case ':': + case ',': + sepchar = c; + state = TSPACE; + break; + default: + item += sep; + item += c; + sep = string(); + state = ITEM; + break; + } + break; + // Separator inside item or at the end of item + case ISEP: + switch (c) + { + case ' ': + case '\t': + if (sep.size() > 1) + throw std::runtime_error("item is followed by more than one separator characters"); + state = TSPACE; + break; + case ':': + case ',': + sep += c; + break; + default: + item += sep; + item += c; + sepchar = 0; + sep = string(); + state = ITEM; + break; + } + break; + case TSPACE: + switch (c) + { + case ' ': + case '\t': + break; + default: + ungetc(c, in); + return sepchar; + } + break; + } + } + if (ferror(in)) + throw std::system_error(errno, std::system_category(), "cannot read from " + pathname); + return EOF; +} + + +// item1, item2, item3: tag1, tag2, tag3 + +//#define TRACE_PARSE +void parse(FILE* in, const std::string& pathname, Fast& out) +{ + string item; + + std::set<string> itemset; + std::set<string> tagset; + int sep; + enum {ITEMS, TAGS} state = ITEMS; + int line = 1; + do + { + sep = parseElement(in, pathname, item); + + if (item.size() != 0) + { + if (state == ITEMS) + itemset |= item; + else + tagset |= item; + } + + switch (sep) + { + case '\n': + line++; + case EOF: + if (!(itemset.empty() && tagset.empty())) + { + if (itemset.empty()) + throw std::runtime_error("no elements before ':' separator"); + if (tagset.empty()) + out.insert(itemset, std::set<std::string>()); + else + out.insert(itemset, tagset); + } + itemset.clear(); + tagset.clear(); + state = ITEMS; + break; + case ':': + if (state == TAGS) + throw std::runtime_error("separator ':' appears twice"); + state = TAGS; + break; + default: + break; + } + } while (sep != EOF); +} + +} +} +} +} diff --git a/ept/debtags/coll/TextFormat.h b/ept/debtags/coll/TextFormat.h new file mode 100644 index 0000000..663711d --- /dev/null +++ b/ept/debtags/coll/TextFormat.h @@ -0,0 +1,66 @@ +#ifndef EPT_DEBTAGS_COLL_TEXTFORMAT_H +#define EPT_DEBTAGS_COLL_TEXTFORMAT_H + +/** \file + * Serialize and deserialize a tagged collection to a text file + */ + +/* + * Copyright (C) 2003--2015 Enrico Zini <enrico@debian.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <cstdio> +#include <string> + +//#define TRACE_PARSE + +namespace ept { +namespace debtags { +namespace coll { +struct Fast; + +namespace textformat { + +/** + * Parse an element from input + * + * @retval item + * The item found on input + * @return + * the trailing separating char, that can be: + * \li input::Input::Eof + * \li '\n' + * \li ':' + * \li ',' + */ +int parseElement(FILE* in, const std::string& pathname, std::string& item); + + +/* + * Parse a tagged collection, sending the results to out. + * + * @param out + * An output iterator accepting a std::pair<string, string> + */ +void parse(FILE* in, const std::string& pathname, Fast& out); + +} +} +} +} + +#endif diff --git a/ept/debtags/coll/fast.cc b/ept/debtags/coll/fast.cc new file mode 100644 index 0000000..43ee143 --- /dev/null +++ b/ept/debtags/coll/fast.cc @@ -0,0 +1,247 @@ +/* + * Fast index for tag data + * + * Copyright (C) 2005--2015 Enrico Zini <enrico@debian.org> + * + * 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 <ept/debtags/coll/fast.h> +#include <ept/debtags/coll/set.h> +#include <ept/debtags/coll/operators.h> + +using namespace std; +using namespace ept::debtags::coll::operators; + +namespace ept { +namespace debtags { +namespace coll { + +void Fast::insert(const std::set<std::string>& items, const std::set<std::string>& tags) +{ + if (tags.empty()) + return; + + for (const auto& i: items) + insert(i, tags); +} + +void Fast::insert(const std::string& item, const std::set<std::string>& tags) +{ + if (tags.empty()) + return; + + auto iter = this->items.find(item); + if (iter == this->items.end()) + this->items.insert(std::make_pair(item, tags)); + else + iter->second |= tags; + + for (typename std::set<std::string>::const_iterator i = tags.begin(); + i != tags.end(); ++i) + { + typename std::map< std::string, std::set<std::string> >::iterator iter = this->tags.find(*i); + if (iter == this->tags.end()) + this->tags.insert(std::make_pair(*i, std::set<std::string>() | item)); + else + iter->second |= item; + } +} + +void Fast::insert(const std::set<std::string>& items, const std::string& tag) +{ + for (typename std::set<std::string>::const_iterator i = items.begin(); + i != items.end(); ++i) + { + typename std::map< std::string, std::set<std::string> >::iterator iter = this->items.find(*i); + if (iter == this->items.end()) + this->items.insert(std::make_pair(*i, std::set<std::string>() | tag)); + else + iter->second |= tag; + } + + typename std::map< std::string, std::set<std::string> >::iterator iter = this->tags.find(tag); + if (iter == this->tags.end()) + this->tags.insert(std::make_pair(tag, items)); + else + iter->second |= items; +} + +std::set<std::string> Fast::getItemsHavingTag(const std::string& tag) const +{ + typename map<std::string, std::set<std::string> >::const_iterator i = tags.find(tag); + if (i != tags.end()) + return i->second; + else + return std::set<std::string>(); +} + +std::set<std::string> Fast::getItemsHavingTags(const std::set<std::string>& tags) const +{ + if (tags.empty()) + return std::set<std::string>(); + + auto i = tags.begin(); + auto res = getItemsHavingTag(*i); + + for (++i ; i != tags.end(); ++i) + res &= getItemsHavingTag(*i); + + return res; +} + + +std::set<std::string> Fast::getTagsOfItem(const std::string& item) const +{ + typename map<std::string, std::set<std::string> >::const_iterator i = items.find(item); + if (i != items.end()) + return i->second; + else + return std::set<std::string>(); +} + +std::set<std::string> Fast::getTaggedItems() const +{ + std::set<std::string> res; + for (typename map<std::string, std::set<std::string> >::const_iterator i = items.begin(); + i != items.end(); i++) + res |= i->first; + return res; +} + +std::set<std::string> Fast::getAllTags() const +{ + std::set<std::string> res; + for (typename map<std::string, std::set<std::string> >::const_iterator i = tags.begin(); + i != tags.end(); i++) + res |= i->first; + return res; +} + +std::vector<std::string> Fast::getAllTagsAsVector() const +{ + std::vector<std::string> res; + for (typename map<std::string, std::set<std::string> >::const_iterator i = tags.begin(); + i != tags.end(); i++) + res.push_back(i->first); + return res; +} + +std::set<std::string> Fast::getTagsImplying(const std::string& tag) const +{ + // tag1 implies tag2 if the itemset of tag1 is a subset of the itemset of tag2 + std::set<std::string> res; + std::set<std::string> itemsToCheck = getItemsHavingTag(tag); + // TODO: choose which one is the most efficient implementation +#if 0 + // Roughly: + // O(n[pkgs per tag] * log(nitems) * log(n[items per pkg]) + n[tags per item] * n[items per tag]) + std::set<std::string> tagsToCheck; + for (std::set<std::string>::const_iterator i = itemsToCheck.begin(); + i != itemsToCheck.end(); ++i) + tagsToCheck |= getTags(*i); + for (std::set<std::string>::const_iterator i = tagsToCheck.begin(); + i != tagsToCheck.end(); ++i) + if (utils::set_contains(itemsToCheck, getItems(*i))) + res |= *i; +#else + // O(ntags * n[items per tag]) + for (typename std::map<std::string, std::set<std::string> >::const_iterator i = tags.begin(); + i != tags.end(); ++i) + if (utils::set_contains(itemsToCheck, getItemsHavingTag(i->first))) + res |= i->first; +#endif + return res - tag; +} + +std::set<std::string> Fast::getItemsExactMatch(const std::set<std::string>& tags) const +{ + std::set<std::string> res = this->getItemsHavingTags(tags); + typename std::set<std::string>::iterator i = res.begin(); + while (i != res.end()) + { + typename std::map<std::string, std::set<std::string> >::const_iterator t = items.find(*i); + if (t != items.end() && t->second != tags) + { + typename std::set<std::string>::iterator j = i; + ++i; + res.erase(j); + } else + ++i; + } + return res; +} + +std::string Fast::findTagWithMaxCardinality(size_t& card) const +{ + card = 0; + std::string res = std::string(); + for (typename std::map<std::string, std::set<std::string> >::const_iterator i = tags.begin(); + i != tags.end(); ++i) + if (i->second.size() > card) + { + card = i->second.size(); + res = i->first; + } + return res; +} + +void Fast::removeTag(const std::string& tag) +{ + typename std::map<std::string, std::set<std::string> >::iterator itag = tags.find(tag); + for (typename std::set<std::string>::const_iterator iitemset = itag->second.begin(); + iitemset != itag->second.end(); ++iitemset) + { + typename std::map<std::string, std::set<std::string> >::iterator iitem = items.find(*iitemset); + iitem->second -= tag; + if (iitem->second.empty()) + items.erase(iitem); + } + tags.erase(itag); +} + +Fast Fast::getChildCollection(const std::string& tag) const +{ + Fast res; + + auto itag = tags.find(tag); + for (const auto& i: itag->second) + { + auto iitem = items.find(i); + res.insert(i, iitem->second); + } + + res.removeTag(tag); + return res; +} + +void Fast::removeTagsWithCardinalityLessThan(size_t card) +{ + typename std::map<std::string, std::set<std::string> >::const_iterator i = tags.begin(); + while (i != tags.end()) + { + if (i->second.size() < card) + { + typename std::map<std::string, std::set<std::string> >::const_iterator j = i; + ++i; + removeTag(j->first); + } else + ++i; + } +} + +} +} +} diff --git a/ept/debtags/coll/fast.h b/ept/debtags/coll/fast.h new file mode 100644 index 0000000..6cc34ff --- /dev/null +++ b/ept/debtags/coll/fast.h @@ -0,0 +1,111 @@ +#ifndef EPT_DEBTAGS_COLL_FAST_H +#define EPT_DEBTAGS_COLL_FAST_H + +/** \file + * Fast index for tag data + */ + +/* + * Copyright (C) 2005--2015 Enrico Zini <enrico@debian.org> + * + * 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 <set> +#include <map> +#include <string> +#include <vector> + +namespace ept { +namespace debtags { +namespace coll { + +/** + * In-memory collection with both item->tags and tag->items mappings. + */ +class Fast +{ +protected: + std::map<std::string, std::set<std::string>> items; + std::map<std::string, std::set<std::string>> tags; + +public: + typedef std::map<std::string, std::set<std::string>>::const_iterator const_iterator; + typedef std::map<std::string, std::set<std::string>>::iterator iterator; + typedef std::map<std::string, std::set<std::string>>::value_type value_type; + typedef std::map<std::string, std::set<std::string>>::const_iterator const_tag_iterator; + typedef std::map<std::string, std::set<std::string>>::iterator tag_iterator; + + const_iterator begin() const { return items.begin(); } + const_iterator end() const { return items.end(); } + iterator begin() { return items.begin(); } + iterator end() { return items.end(); } + + const_tag_iterator tagBegin() const { return tags.begin(); } + const_tag_iterator tagEnd() const { return tags.end(); } + tag_iterator tagBegin() { return tags.begin(); } + tag_iterator tagEnd() { return tags.end(); } + + void insert(const std::string& item, const std::set<std::string>& tags); + void insert(const std::set<std::string>& items, const std::string& tag); + void insert(const std::set<std::string>& items, const std::set<std::string>& tags); + + void clear() { items.clear(); tags.clear(); } + + std::set<std::string> getTagsOfItem(const std::string& item) const; + std::set<std::string> getItemsHavingTag(const std::string& tag) const; + + /** + * Get the items which are tagged with at least the tags `tags' + * + * \return + * The items found, or an empty set if no items have that tag + */ + std::set<std::string> getItemsHavingTags(const std::set<std::string>& tags) const; + + bool empty() const { return items.empty(); } + + bool hasItem(const std::string& item) const { return items.find(item) != items.end(); } + bool hasTag(const std::string& tag) const { return tags.find(tag) != tags.end(); } + std::set<std::string> getTaggedItems() const; + std::set<std::string> getAllTags() const; + std::vector<std::string> getAllTagsAsVector() const; + + unsigned int itemCount() const { return items.size(); } + unsigned int tagCount() const { return tags.size(); } + + // tag1 implies tag2 if the itemset of tag1 is a subset of the itemset of + // tag2 + std::set<std::string> getTagsImplying(const std::string& tag) const; + + // Return the items which have the exact tagset 'tags' + std::set<std::string> getItemsExactMatch(const std::set<std::string>& tags) const; + + std::string findTagWithMaxCardinality(size_t& card) const; + + /** + * Return the collection with only those items that have this tag, but with + * the given tag removed + */ + Fast getChildCollection(const std::string& tag) const; + + void removeTag(const std::string& tag); + void removeTagsWithCardinalityLessThan(size_t card); +}; + +} +} +} +#endif diff --git a/ept/debtags/coll/operators.h b/ept/debtags/coll/operators.h new file mode 100644 index 0000000..2fecbad --- /dev/null +++ b/ept/debtags/coll/operators.h @@ -0,0 +1,201 @@ +// -*- C++ -*- + +#ifndef EPT_DEBTAGS_COLL_OPERATORS_H +#define EPT_DEBTAGS_COLL_OPERATORS_H + +#include <set> +#include <algorithm> + +namespace ept { +namespace debtags { +namespace coll { +namespace operators { + +/* +template< typename S, typename VT > struct IsContainer { + typedef S T; +}; + +template< typename S > +typename IsContainer< S, typename S::value_type >::T operator &&( const S &a, const S &b ) { + S ret; + std::set_intersection( a.begin(), a.end(), b.begin(), b.end(), + std::inserter( ret, ret.begin() ) ); + return ret; +} +*/ + +template< typename T > +T operator+( const T &i, typename T::difference_type o ) { + T r = i; + std::advance( r, o ); + return r; +} + +template< typename T > +std::set< T > operator &( const std::set< T > &a, const std::set< T > &b ) { + std::set< T > ret; + std::set_intersection( a.begin(), a.end(), b.begin(), b.end(), + std::inserter( ret, ret.begin() ) ); + return ret; +} + +template< typename T > +std::set< T > operator &( const std::set< T > &a, const T &b ) { + std::set< T > ret; + if ( a.find( b ) != a.end() ) { + std::set< T > r; + r.insert( b ); + return r; + } + return std::set< T >(); +} + +template< typename T > +std::set< T > operator |( const std::set< T > &a, const T& item ) { + std::set< T > ret = a; + ret.insert(item); + return ret; +} + +template< typename T > +std::set< T > operator |( const std::set< T > &a, const std::set< T > &b ) { + std::set< T > ret; + std::set_union( a.begin(), a.end(), b.begin(), b.end(), + std::inserter( ret, ret.begin() ) ); + return ret; +} + +template< typename T > +std::set< T > operator -( const std::set< T > &a, const std::set< T > &b ) { + std::set< T > ret; + std::set_difference( a.begin(), a.end(), b.begin(), b.end(), + std::inserter(ret, ret.begin() ) ); + return ret; +} + +template< typename T > +std::set< T > operator -( const std::set< T > &a, const T& item ) { + std::set< T > ret = a; + ret.erase(item); + return ret; +} + +template< typename T > +std::set< T > &operator|=( std::set< T > &a, const T& item ) +{ + a.insert(item); + return a; +} + +// General case +template< typename T, typename SEQ > +std::set< T > &operator|=( std::set< T > &a, const SEQ& items ) +{ + for (typename SEQ::const_iterator i = items.begin(); + i != items.end(); ++i) + a.insert(*i); + return a; +} + +// Little optimization in case a is empty +template< typename T > +std::set< T > &operator |=( std::set< T > &a, const std::set< T > &b ) { + if (a.empty()) + return a = b; + + for (typename std::set<T>::const_iterator i = b.begin(); + i != b.end(); ++i) + a.insert(*i); + return a; +} + +// General case, but assumes that b is sorted +template< typename T, typename SEQ > +std::set< T > &operator &=( std::set< T > &a, const SEQ& b ) { + // Little optimization: if b is empty, we avoid a run through a + if (b.empty()) + { + a.clear(); + return a; + } + + typename std::set<T>::iterator ia = a.begin(); + typename SEQ::const_iterator ib = b.begin(); + while (ia != a.end()) + { + if (ib != b.end() && *ib < *ia) + { + ++ib; + } + else if (ib == b.end() || *ia != *ib) + { + typename std::set<T>::iterator tmp = ia; + ++ia; + a.erase(tmp); + } + else + { + ++ia; + ++ib; + } + } + return a; +} + +template< typename T > +std::set< T > &operator-=( std::set< T > &a, const T& item ) +{ + a.erase(item); + return a; +} + +// General case, but works only if b is sorted +template< typename T, typename SEQ > +std::set< T > &operator -=( std::set< T > &a, const SEQ& b ) +{ + typename std::set<T>::iterator ia = a.begin(); + typename SEQ::const_iterator ib = b.begin(); + while (ia != a.end() && ib != b.end()) + { + if (*ia == *ib) + { + typename std::set<T>::iterator tmp = ia; + ++ia; + ++ib; + a.erase(tmp); + } + else if (*ia < *ib) + ++ia; + else + ++ib; + } + return a; +} + +template< typename T > +bool operator<=( const T &a, const std::set< T > &b ) { + return b.find( a ) != b.end(); +} + +template< typename T > +bool operator<=( const std::set< T > &a, const std::set< T > &b ) { + typename std::set<T>::const_iterator x = a.begin(); + + for ( typename std::set<T>::const_iterator y = b.begin(); y != b.end(); ++y ) + if ( x == a.end() ) + return true; + else if (*x == *y) + ++x; + else if (*x < *y) + return false; + + return x == a.end(); +} + +} +} +} +} + +#endif diff --git a/ept/debtags/coll/set.h b/ept/debtags/coll/set.h new file mode 100644 index 0000000..bcc9934 --- /dev/null +++ b/ept/debtags/coll/set.h @@ -0,0 +1,91 @@ +#ifndef TAGCOLL_UTILS_SET_H +#define TAGCOLL_UTILS_SET_H + +/** \file + * Extra useful set operations + */ + +/* + * Copyright (C) 2003,2004,2005,2006 Enrico Zini <enrico@debian.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <ept/debtags/coll/operators.h> +#include <set> + +namespace ept { +namespace debtags { +namespace coll { +namespace utils { + +template<typename T> +int set_distance(const std::set<T>& set1, const std::set<T>& set2) +{ + int res = 0; + int intCount = 0; + + typename std::set<T>::const_iterator a = set1.begin(); + typename std::set<T>::const_iterator b = set2.begin(); + + while (a != set1.end() || b != set2.end()) + if ((b == set2.end()) || (a != set1.end() && *a < *b)) + { + res++; + a++; + } + else if ((a == set1.end()) || (b != set2.end() && *b < *a)) + { + res++; + b++; + } + else + { + a++; + b++; + intCount++; + } + + return intCount ? res : -1; +} + +template<typename T> +bool set_contains(const std::set<T>& set1, const std::set<T>& set2) +{ + typename std::set<T>::const_iterator b = set2.begin(); + + for (typename std::set<T>::const_iterator a = set1.begin(); a != set1.end(); ++a) + if (b == set2.end()) + return true; + else if (*a == *b) + b++; + else if (*b < *a) + return false; + + return b == set2.end(); +} + +template<typename T> +bool set_contains(const std::set<T>& set1, const T& item) +{ + return set1.find(item) != set1.end(); +} + +} +} +} +} + +#endif diff --git a/ept/debtags/debtags-test.cc b/ept/debtags/debtags-test.cc new file mode 100644 index 0000000..9ed1eb6 --- /dev/null +++ b/ept/debtags/debtags-test.cc @@ -0,0 +1,163 @@ +#include "ept/test.h" +#include "debtags.h" +#include "coll/operators.h" +#include <cstdio> + +using namespace std; +using namespace ept; +using namespace ept::debtags; +using namespace ept::tests; +using namespace ept::debtags::coll::operators; + +#define testfile TEST_ENV_DIR "debtags/package-tags" + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("iterate", []() { + EnvOverride eo("DEBTAGS_TAGS", testfile); + Debtags debtags; + for (Debtags::const_iterator i = debtags.begin(); i != debtags.end(); ++i) + { + *i; + i->first; + i->second; + } + }); + + add_method("lookup_tags", []() { + EnvOverride eo("DEBTAGS_TAGS", testfile); + Debtags debtags; + string p("debtags"); + std::set<std::string> tags = debtags.getTagsOfItem(p); + wassert(actual(tags.empty()).isfalse()); + +#if 0 + for ( std::set< Tag >::iterator i = tags.begin(); i != tags.end(); ++ i ) { + std::cerr << i->id() << ": " << i->fullname() << std::endl; + } + std::cerr << "---" << std::endl; + Tag t = voc().tagByName( "interface::commandline" ); + std::cerr << t.id() << ": " << t.fullname() << std::endl; +#endif + + wassert(actual(tags.size()) == 8u); + wassert(actual(tags.find("devel::buildtools") != tags.end()).istrue()); + wassert(actual(tags.find("implemented-in::c++") != tags.end()).istrue()); + wassert(actual(tags.find("interface::commandline") != tags.end()).istrue()); + wassert(actual(tags.find("role::program") != tags.end()).istrue()); + wassert(actual(tags.find("scope::application") != tags.end()).istrue()); + wassert(actual(tags.find("suite::debian") != tags.end()).istrue()); + wassert(actual(tags.find("use::searching") != tags.end()).istrue()); + wassert(actual(tags.find("works-with::software:package") != tags.end()).istrue()); + }); + + add_method("lookup_packages", []() { + using namespace std; + EnvOverride eo("DEBTAGS_TAGS", testfile); + Debtags debtags; + + /* Get the 'debtags' package */ + string p("debtags"); + + /* Get its tags */ + std::set<std::string> tags = debtags.getTagsOfItem(p); + wassert(actual(tags.empty()).isfalse()); + + /* + cerr << "Intersection size: " << endl; + using namespace wibble::operators; + std::set<Tag>::const_iterator dbgi = tags.begin(); + cerr << "* " << dbgi->fullname() << ": " << dbgi->id() << endl; + std::set<int> dbgres = debtags.tagdb().getItemsHavingTag(dbgi->id()); + std::set<Package> dbgpres = debtags.getItemsHavingTag(*dbgi); + cerr << " #pkgs " << dbgres.size() << " == " << dbgpres.size() << endl; + cerr << " #isec " << dbgres.size() << " == " << dbgpres.size() << endl; + cerr << " "; ppset(dbgpres); cerr << endl; + cerr << " "; piset(dbgres); cerr << endl; + for (++dbgi ; dbgi != tags.end(); ++dbgi) + { + cerr << "* " << dbgi->fullname() << ": " << dbgi->id() << endl; + std::set<Package> dbgpkgs = debtags.getItemsHavingTag(*dbgi); + std::set<int> dbgids = debtags.tagdb().getItemsHavingTag(dbgi->id()); + cerr << " "; ppset(dbgpkgs); cerr << endl; + cerr << " "; piset(dbgids); cerr << endl; + cerr << " #pkgs " << dbgpkgs.size() << " == " << dbgids.size() << endl; + dbgres &= dbgids; + dbgpres &= dbgpkgs; + cerr << " #isec " << dbgres.size() << " == " << dbgpres.size() << endl; + } + cerr << " " << dbgres.size() << endl << "Results: " << endl; + for (std::set<int>::const_iterator i = dbgres.begin(); i != dbgres.end(); ++i) + cerr << " " << *i << endl; + */ + + + // cerr << "Tags of debtags: "; + // for (std::set<Tag>::const_iterator i = tags.begin(); i != tags.end(); ++i) + // { + // cerr << " " + i->fullname() << endl; + // std::set<Package> packages = debtags.getItemsHavingTag(*i); + // for (std::set<Package>::const_iterator p = packages.begin(); + // p != packages.end(); ++p) + // cerr << " PKG " << p->name() << endl; + // } + // cerr << endl; + + /* Get the items for the tagset of 'debtags' */ + std::set<string> packages = debtags.getItemsHavingTags(tags); + //cerr << packages.size() << endl; + wassert(actual(packages.empty()).isfalse()); + /* + for ( std::set< Package >::iterator i = packages.begin(); i != packages.end(); ++ i ) + std::cerr << i->name() << std::endl; + std::cerr << "---" << std::endl; + std::cerr << p.name() << std::endl; + */ + /* They should at least contain 'debtags' */ + wassert(actual(p <= packages).istrue()); + + /* Get one of the tags of 'debtags' */ + std::string tag = *tags.begin(); + + /* Get its items */ + { + /* Need this workaround until I figure out how to tell the new GCC + * that TagDB is a TDBReadonlyDiskIndex and should behave as such + */ + std::set<std::string> ts; + ts.insert(tag); + packages = debtags.getItemsHavingTags(ts); + } + //packages = c.debtags().tagdb().getItems(tag); + wassert(actual(packages.empty()).isfalse()); + /* They should at least contain 'debtags' */ + wassert(actual(p <= packages).istrue()); + + //c.debtags().getTags(""); // XXX HACK AWW! + }); + + add_method("empty", []() { + // If there is no data, Debtags should work as an empty collection + EnvOverride eo("DEBTAGS_TAGS", "./empty/notags"); + Debtags empty; + + wassert(actual(empty.begin() == empty.end()).istrue()); + wassert(actual(empty.timestamp()) == 0); + wassert(actual(empty.hasData()).isfalse()); + + set<std::string> res = empty.getTagsOfItem("apt"); + wassert(actual(res.empty()).istrue()); + + res = empty.getAllTags(); + wassert(actual(res.empty()).istrue()); + }); + } +} tests("debtags"); + +} diff --git a/ept/debtags/debtags.cc b/ept/debtags/debtags.cc index 4ebf5bf..62cc382 100644 --- a/ept/debtags/debtags.cc +++ b/ept/debtags/debtags.cc @@ -6,7 +6,7 @@ /* * System tag database * - * Copyright (C) 2003-2008 Enrico Zini <enrico@debian.org> + * Copyright (C) 2003-2015 Enrico Zini <enrico@debian.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,28 +23,18 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <ept/debtags/debtags.h> - -#include <tagcoll/patch.h> -#include <tagcoll/coll/simple.h> -#include <tagcoll/input/stdio.h> -#include <tagcoll/TextFormat.h> - -#include <wibble/sys/fs.h> -#include <wibble/string.h> - +#include "debtags.h" +#include "ept/utils/sys.h" +#include "coll/TextFormat.h" +#include <system_error> #include <iostream> #include <sstream> - #include <sys/wait.h> // WIFEXITED WEXITSTATUS #include <sys/types.h> // getpwuid, getuid #include <pwd.h> // getpwuid #include <unistd.h> // getuid - using namespace std; -using namespace tagcoll; -using namespace wibble; namespace ept { namespace debtags { @@ -53,7 +43,7 @@ Debtags::Debtags() : m_timestamp(0) { string src = pathname(); - if (!sys::fs::exists(src)) + if (!sys::exists(src)) return; load(src); } @@ -67,13 +57,21 @@ Debtags::Debtags(const std::string& pathname) void Debtags::load(const std::string& pathname) { // Read uncompressed data - tagcoll::input::Stdio in(pathname); + FILE* in = fopen(pathname.c_str(), "rt"); + if (!in) + throw std::system_error(errno, std::system_category(), "cannot open " + pathname); // Read the collection - tagcoll::textformat::parse(in, inserter(*this)); + try { + coll::textformat::parse(in, pathname, *this); + } catch (...) { + fclose(in); + throw; + } + fclose(in); // Read the timestamp - m_timestamp = sys::fs::timestamp(pathname, 0); + m_timestamp = sys::timestamp(pathname, 0); } string Debtags::pathname() @@ -85,12 +83,3 @@ string Debtags::pathname() } } - -#include <tagcoll/coll/simple.tcc> -#include <tagcoll/coll/fast.tcc> -#include <tagcoll/TextFormat.tcc> - -// Explicit template instantiations for our stuff -template class tagcoll::coll::Fast<std::string, std::string>; - -// vim:set ts=4 sw=4: diff --git a/ept/debtags/debtags.h b/ept/debtags/debtags.h index c908f0d..52d9347 100644 --- a/ept/debtags/debtags.h +++ b/ept/debtags/debtags.h @@ -26,18 +26,11 @@ #ifndef EPT_DEBTAGS_DEBTAGS_H #define EPT_DEBTAGS_DEBTAGS_H -#include <tagcoll/coll/base.h> -#include <tagcoll/coll/fast.h> +#include <ept/debtags/coll/fast.h> #include <string> namespace ept { namespace debtags { -class Debtags; -} -} - -namespace ept { -namespace debtags { /** * Access the on-disk Debtags tag database. @@ -51,7 +44,7 @@ namespace debtags { * It is possible to get a reference to the Vocabulary object using the * vocabulary() method. */ -class Debtags : public tagcoll::coll::Fast<std::string, std::string> +class Debtags : public coll::Fast { protected: // User rc directory to store patches @@ -63,8 +56,8 @@ protected: void load(const std::string& pathname); public: - typedef tagcoll::coll::Fast<std::string, std::string> coll_type; - typedef std::pair< std::string, std::set<std::string> > value_type; + typedef ept::debtags::coll::Fast coll_type; + typedef std::pair< std::string, std::set<std::string> > value_type; /// Create a Debtags object, reading the system database Debtags(); @@ -90,7 +83,6 @@ public: static std::string pathname(); }; - } } diff --git a/ept/debtags/debtags.test.h b/ept/debtags/debtags.test.h deleted file mode 100644 index 54b57eb..0000000 --- a/ept/debtags/debtags.test.h +++ /dev/null @@ -1,206 +0,0 @@ -// -*- mode: c++; tab-width: 4; indent-tabs-mode: t -*- -/** - * @file - * @author Enrico Zini (enrico) <enrico@enricozini.org> - */ - -/* - * Test for the Debtags data provider - * - * Copyright (C) 2003-2007 Enrico Zini <enrico@debian.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include <ept/debtags/debtags.h> - -#include <tagcoll/coll/simple.h> -#include <tagcoll/stream/sink.h> -#include <tagcoll/patch.h> - -#include <wibble/operators.h> - -#include <ept/test.h> -#include <cstdio> - -#ifndef EPT_DEBTAGS_TESTH -#define EPT_DEBTAGS_TESTH - -using namespace tagcoll; -using namespace std; -using namespace ept; -using namespace ept::debtags; -using namespace wibble::operators; - -#define testfile TEST_ENV_DIR "debtags/package-tags" - -struct TestDebtags : DebtagsTestEnvironment -{ - TestDebtags() {} - - Test _1() - { - EnvOverride eo("DEBTAGS_TAGS", testfile); - Debtags debtags; - for (Debtags::const_iterator i = debtags.begin(); i != debtags.end(); ++i) - { - *i; - i->first; - i->second; - } - } - - Test _2() - { - EnvOverride eo("DEBTAGS_TAGS", testfile); - Debtags debtags; - string p("debtags"); - std::set<std::string> tags = debtags.getTagsOfItem(p); - assert( !tags.empty() ); - -#if 0 - for ( std::set< Tag >::iterator i = tags.begin(); i != tags.end(); ++ i ) { - std::cerr << i->id() << ": " << i->fullname() << std::endl; - } - std::cerr << "---" << std::endl; - Tag t = voc().tagByName( "interface::commandline" ); - std::cerr << t.id() << ": " << t.fullname() << std::endl; -#endif - - assert_eq( tags.size(), 8u ); - assert( tags.find("devel::buildtools") != tags.end() ); - assert( tags.find("implemented-in::c++") != tags.end() ); - assert( tags.find("interface::commandline") != tags.end() ); - assert( tags.find("role::program") != tags.end() ); - assert( tags.find("scope::application") != tags.end() ); - assert( tags.find("suite::debian") != tags.end() ); - assert( tags.find("use::searching") != tags.end() ); - assert( tags.find("works-with::software:package") != tags.end() ); -} - - Test _3() - { - using namespace std; - EnvOverride eo("DEBTAGS_TAGS", testfile); - Debtags debtags; - - /* Get the 'debtags' package */ - string p("debtags"); - - /* Get its tags */ - std::set<std::string> tags = debtags.getTagsOfItem(p); - assert(!tags.empty()); - - /* - cerr << "Intersection size: " << endl; - using namespace wibble::operators; - std::set<Tag>::const_iterator dbgi = tags.begin(); - cerr << "* " << dbgi->fullname() << ": " << dbgi->id() << endl; - std::set<int> dbgres = debtags.tagdb().getItemsHavingTag(dbgi->id()); - std::set<Package> dbgpres = debtags.getItemsHavingTag(*dbgi); - cerr << " #pkgs " << dbgres.size() << " == " << dbgpres.size() << endl; - cerr << " #isec " << dbgres.size() << " == " << dbgpres.size() << endl; - cerr << " "; ppset(dbgpres); cerr << endl; - cerr << " "; piset(dbgres); cerr << endl; - for (++dbgi ; dbgi != tags.end(); ++dbgi) - { - cerr << "* " << dbgi->fullname() << ": " << dbgi->id() << endl; - std::set<Package> dbgpkgs = debtags.getItemsHavingTag(*dbgi); - std::set<int> dbgids = debtags.tagdb().getItemsHavingTag(dbgi->id()); - cerr << " "; ppset(dbgpkgs); cerr << endl; - cerr << " "; piset(dbgids); cerr << endl; - cerr << " #pkgs " << dbgpkgs.size() << " == " << dbgids.size() << endl; - dbgres &= dbgids; - dbgpres &= dbgpkgs; - cerr << " #isec " << dbgres.size() << " == " << dbgpres.size() << endl; - } - cerr << " " << dbgres.size() << endl << "Results: " << endl; - for (std::set<int>::const_iterator i = dbgres.begin(); i != dbgres.end(); ++i) - cerr << " " << *i << endl; - */ - - -// cerr << "Tags of debtags: "; -// for (std::set<Tag>::const_iterator i = tags.begin(); i != tags.end(); ++i) -// { -// cerr << " " + i->fullname() << endl; -// std::set<Package> packages = debtags.getItemsHavingTag(*i); -// for (std::set<Package>::const_iterator p = packages.begin(); -// p != packages.end(); ++p) -// cerr << " PKG " << p->name() << endl; -// } -// cerr << endl; - - /* Get the items for the tagset of 'debtags' */ - std::set<string> packages = debtags.getItemsHavingTags(tags); - //cerr << packages.size() << endl; - assert(!packages.empty()); - /* - for ( std::set< Package >::iterator i = packages.begin(); i != packages.end(); ++ i ) - std::cerr << i->name() << std::endl; - std::cerr << "---" << std::endl; - std::cerr << p.name() << std::endl; - */ - /* They should at least contain 'debtags' */ - assert( p <= packages ); - - /* Get one of the tags of 'debtags' */ - std::string tag = *tags.begin(); - - /* Get its items */ - { - /* Need this workaround until I figure out how to tell the new GCC - * that TagDB is a TDBReadonlyDiskIndex and should behave as such - */ - std::set<std::string> ts; - ts.insert(tag); - packages = debtags.getItemsHavingTags(ts); - } - //packages = c.debtags().tagdb().getItems(tag); - assert(!packages.empty()); - /* They should at least contain 'debtags' */ - assert( p <= packages ); - - //c.debtags().getTags(""); // XXX HACK AWW! -} - - // If there is no data, Debtags should work as an empty collection - Test _4() - { - EnvOverride eo("DEBTAGS_TAGS", "./empty/notags"); - Debtags empty; - - assert(empty.begin() == empty.end()); - assert_eq(empty.timestamp(), 0); - assert(!empty.hasData()); - - set<std::string> res = empty.getTagsOfItem("apt"); - assert(res.empty()); - // TODO: currently does not compile because of a bug in tagcoll - //res = empty.getTagsOfItems(wibble::singleton(string("apt"))); - //assert(res.empty()); - - res = empty.getAllTags(); - assert(res.empty()); -} - -}; - -#include <tagcoll/coll/simple.tcc> - -#endif - -// vim:set ts=4 sw=4: diff --git a/ept/debtags/expression.test.h b/ept/debtags/expression.test.h deleted file mode 100644 index b3789ab..0000000 --- a/ept/debtags/expression.test.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Match tag expressions against sets of Debtags Tags - * - * Copyright (C) 2005,2006,2007 Enrico Zini <enrico@debian.org> - * - * 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 <wibble/test.h> -#include <tagcoll/expression.h> -#include <ept/debtags/vocabulary.h> - -#include "debtags.test.h" - -using namespace tagcoll; -using namespace std; -using namespace ept::debtags; - -struct TestExpression : DebtagsTestEnvironment { - Vocabulary voc; - - Test _1() -{ - set<std::string> test; - test.insert("use::editing"); - test.insert("use::viewing"); - test.insert("works-with::text"); - - assert_eq(test.size(), 3u); - - Expression e1("use::editing"); - assert(e1(test)); - - Expression e2("use::editing && use::viewing"); - assert(e2(test)); - - e1 = Expression("!use::editing"); - assert(!e1(test)); - - e1 = Expression("use::editing || sugo"); - assert(e1(test)); - - e1 = Expression("use::editing && !sugo"); - assert(e1(test)); - - e1 = Expression("use::editing && !use::viewing"); - assert(!e1(test)); - - e1 = Expression("(use::editing || sugo) && (use::viewing && works-with::text)"); - assert(e1(test)); - - e1 = Expression("!(use::editinuse::editingra && works-with::text)"); - assert(e1(test)); - - e1 = Expression("works-with::*"); - assert(e1(test)); - - e1 = Expression("*::text"); - assert(e1(test)); - - e1 = Expression("!*::antani"); - assert(e1(test)); - - e1 = Expression("*::antani"); - assert(!e1(test)); -} - -}; - -// vim:set ts=4 sw=4: diff --git a/ept/debtags/maint/debdbparser.cc b/ept/debtags/maint/debdbparser.cc index 259cae4..51f2e88 100644 --- a/ept/debtags/maint/debdbparser.cc +++ b/ept/debtags/maint/debdbparser.cc @@ -19,14 +19,9 @@ */ #include <ept/debtags/maint/debdbparser.h> - -#include <tagcoll/input/base.h> - #include <map> -#include <ctype.h> - -// using namespace std; -using namespace tagcoll; +#include <cctype> +#include <system_error> namespace ept { namespace debtags { @@ -35,9 +30,9 @@ namespace debtags { // Returns the number of '\n' encountered int DebDBParser::eatSpacesAndEmptyLines() { - int res = 0; - int c; - while ((c = in.nextChar()) != input::Input::Eof && (isblank(c) || c == '\n')) + int res = 0; + int c; + while ((c = getc(in)) != EOF && (isblank(c) || c == '\n')) if (c == '\n') { isBOL = true; @@ -46,10 +41,14 @@ int DebDBParser::eatSpacesAndEmptyLines() } else isBOL = false; - if (c == input::Input::Eof) - isEOF = true; - else - in.pushChar(c); + if (c == EOF) + { + if (ferror(in)) + throw std::system_error(errno, std::system_category(), "cannot read from " + pathname); + isEOF = true; + } + else + ungetc(c, in); return res; } @@ -57,24 +56,26 @@ int DebDBParser::eatSpacesAndEmptyLines() // Get the ^([A-Za-z0-9]+) field name std::string DebDBParser::getFieldName() { - if (! isBOL) - throw exception::Parser(in, "field must start at the beginning of the line"); + if (! isBOL) + throw std::runtime_error("field must start at the beginning of the line"); std::string res; - int c; - while ((c = in.nextChar()) != input::Input::Eof && (isalnum(c) || c == '-')) - res += c; - - if (c == input::Input::Eof) - { - isEOF = true; - if (!res.empty()) - throw exception::Parser(in, "field is truncated at end of file. Last line begins with: \"" + res + "\n"); - } else - in.pushChar(c); - - return res; + int c; + while ((c = getc(in)) != EOF && (isalnum(c) || c == '-')) + res += c; + + if (c == EOF) + { + if (ferror(in)) + throw std::system_error(errno, std::system_category(), "cannot read from " + pathname); + isEOF = true; + if (!res.empty()) + throw std::runtime_error("field is truncated at end of file. Last line begins with: \"" + res + "\n"); + } else + ungetc(c, in); + + return res; } // Eat the \s*: characters that divide the field name and the field @@ -83,18 +84,20 @@ void DebDBParser::eatFieldSep() { int c; - while ((c = in.nextChar()) != input::Input::Eof && isblank(c)) + while ((c = getc(in)) != EOF && isblank(c)) ; if (c != ':') { - if (c == input::Input::Eof) - { - isEOF = true; - throw exception::Parser(in, "field is truncated at end of file"); - } else { - throw exception::Parser(in, std::string("invalid character `") + (char)c + "' expecting `:'"); - } + if (c == EOF) + { + if (ferror(in)) + throw std::system_error(errno, std::system_category(), "cannot read from " + pathname); + isEOF = true; + throw std::runtime_error("field is truncated at end of file"); + } else { + throw std::runtime_error(std::string("invalid character `") + (char)c + "' expecting `:'"); + } } } @@ -103,31 +106,35 @@ void DebDBParser::appendFieldBody(std::string& body) { int c; - // Skip leading spaces - while ((c = in.nextChar()) != input::Input::Eof && isblank(c)) - ; + // Skip leading spaces + while ((c = getc(in)) != EOF && isblank(c)) + ; - // Get the body part - for ( ; c != input::Input::Eof && c != '\n'; c = in.nextChar()) - body += c; + // Get the body part + for ( ; c != EOF && c != '\n'; c = getc(in)) + body += c; // Delete trailing spaces size_t end = body.find_last_not_of(" \t"); if (end != std::string::npos) body.resize(end + 1); - if (c == input::Input::Eof) - isEOF = true; - else - { - //line++; - isBOL = true; - } + if (c == EOF) + { + if (ferror(in)) + throw std::system_error(errno, std::system_category(), "cannot read from " + pathname); + isEOF = true; + } + else + { + //line++; + isBOL = true; + } } -DebDBParser::DebDBParser(input::Input& input) : - in(input), isBOL(true), isEOF(false) +DebDBParser::DebDBParser(FILE* input, const std::string& pathname) + : in(input), pathname(pathname), isBOL(true), isEOF(false) { // Go at the start of the next record eatSpacesAndEmptyLines(); diff --git a/ept/debtags/maint/debdbparser.h b/ept/debtags/maint/debdbparser.h index e48faec..d26aa5e 100644 --- a/ept/debtags/maint/debdbparser.h +++ b/ept/debtags/maint/debdbparser.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2003--2007 Enrico Zini <enrico@debian.org> + * Copyright (C) 2003--2015 Enrico Zini <enrico@debian.org> * * 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 @@ -23,17 +23,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <tagcoll/input/base.h> - -// TODO: is there a way to forward-declare this? #include <map> -namespace tagcoll { -namespace input { -class Input; -} -} - namespace ept { namespace debtags { @@ -52,7 +43,8 @@ public: class DebDBParser { protected: - tagcoll::input::Input& in; + FILE* in; + std::string pathname; bool isBOL; bool isEOF; @@ -73,10 +65,9 @@ protected: public: typedef std::map<std::string, std::string> Record; - DebDBParser(tagcoll::input::Input& input); + DebDBParser(FILE* input, const std::string& pathname); - const std::string& fileName() const throw () { return in.fileName(); } - int lineNumber() const throw () { return in.lineNumber(); } + const std::string& fileName() const throw () { return pathname; } // Read a record and positions itself at the start of the next one // Returns false when there are no more records available diff --git a/ept/debtags/vocabulary-test.cc b/ept/debtags/vocabulary-test.cc new file mode 100644 index 0000000..682e521 --- /dev/null +++ b/ept/debtags/vocabulary-test.cc @@ -0,0 +1,195 @@ +#include "ept/test.h" +#include "vocabulary.h" +#include "coll/set.h" +#include "ept/test.h" + +using namespace std; +using namespace ept::debtags::coll::utils; +using namespace ept::debtags; +using namespace ept::tests; + +#define testfile "debtags/vocabulary" + +namespace { + +class Tests : public TestCase +{ + using TestCase::TestCase; + + void register_tests() override + { + add_method("load", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; // this will throw if it failed to load + }); + + add_method("has_facet", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + assert_true( tags.hasFacet( "works-with" ) ); + assert_true( !tags.hasFacet( "blah" ) ); + }); + + add_method("has_tag", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + assert_true( tags.hasTag( "works-with::people" ) ); + assert_true( !tags.hasTag( "works-with::foobar" ) ); + }); + + add_method("tagdata", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + const voc::TagData *people = tags.tagData( "works-with::people" ), + *foobar = tags.tagData( "works-with::foobar" ), + *blahg = tags.tagData( "works-with::blahg" ), + *text = tags.tagData( "works-with::text" ), + *people2 = tags.tagData( "works-with::people" ); + assert_true( people != foobar ); + assert_true( people != text ); + assert_true( people != blahg ); + assert_true( foobar == blahg ); + assert_true( foobar == foobar ); + assert_true( people == people2 ); + assert_true( people == people ); + }); + + add_method("tags", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + std::string a = "works-with::people", + b = "works-with::foobar"; + std::set<std::string> s = tags.tags(), + f = tags.tags( "works-with" ), + n = tags.tags( "nonsense" ); + assert_true( set_contains(s, a) ); + assert_true( set_contains(f, a) ); + assert_true( set_contains(s, f) ); + assert_true( !set_contains(s, b) ); + assert_true( !set_contains(f, b) ); + assert_true( n.empty() ); + }); + + add_method("facetdata", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + const voc::FacetData* f = tags.facetData( "works-with" ); + assert_true(f); + wassert(actual(f->name) == "works-with"); + + const voc::TagData* t = tags.tagData( "works-with::people" ); + assert_true(t); + wassert(actual(t->name) == "works-with::people"); + }); + + add_method("facettags", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + const voc::FacetData* f = tags.facetData( "works-with" ); + std::set<std::string> x = tags.tags( "works-with" ); + assert_true( x == f->tags() ); + }); + + add_method("missing_facet", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + const voc::FacetData* f = tags.facetData( "does-not-work-with" ); + assert_true(!f); + }); + + add_method("missing_facet1", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + const voc::FacetData* f = tags.facetData( "legacy" ); + assert_true(f); + wassert(actual(f->shortDescription()) == ""); + wassert(actual(f->longDescription()) == ""); + //wassert(actual(f.shortDescription( "weehee" )) == "weehee"); + }); + + add_method("one_letter_tag", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + // assert_true that one-character tag names are parsed correctly + assert_true( tags.hasTag( "implemented-in::c" ) ); + }); + + add_method("iterate_facets", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + // assert_true that all facets are somehow working + std::set<std::string> facets = tags.facets(); + + for (std::set<std::string>::const_iterator i = facets.begin(); + i != facets.end(); i++) + { + const voc::FacetData* f = tags.facetData(*i); + assert_true(f); + } + }); + + add_method("iterate_tags", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary voc; + + // assert_true that all tags are somehow working + std::set<std::string> tags = voc.tags(); + for (std::set<std::string>::const_iterator i = tags.begin(); + i != tags.end(); i++) + { + const voc::TagData* t = voc.tagData(*i); + assert_true(t); + } + }); + + add_method("first_last", []() { + // Check for correctness of the first and last tag in the vocabulary + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + const voc::TagData* first = tags.tagData("accessibility::TODO"); + assert_true(first); + wassert(actual(first->name) == string("accessibility::TODO")); + wassert(actual(first->shortDescription()) == string("Need an extra tag")); + + const voc::TagData* last = tags.tagData("x11::xserver"); + assert_true(last); + wassert(actual(last->name) == string("x11::xserver")); + wassert(actual(last->shortDescription()) == string("X Server")); + }); + + add_method("get_all_tags", []() { + EnvOverride eo("DEBTAGS_VOCABULARY", testfile); + Vocabulary tags; + + // check that we're seeing all the tags for a facet + std::set<std::string> t = tags.tags("accessibility"); + wassert(actual(t.size()) == 10u); + + t = tags.tags("works-with-format"); + wassert(actual(t.size()) == 33u); + }); + + add_method("empty", []() { + // If there is no data, Vocabulary should work as an empty vocabulary + EnvOverride eo("DEBTAGS_VOCABULARY", "./empty/novocabularyhere"); + Vocabulary empty; + + assert_true(!empty.hasData()); + + set<std::string> facets = empty.facets(); + wassert(actual(facets.size()) == 0u); + + set<std::string> tags = empty.tags(); + wassert(actual(tags.size()) == 0u); + }); + } +} tests("debtags_vocabulary"); + +} diff --git a/ept/debtags/vocabulary.cc b/ept/debtags/vocabulary.cc index 504fab2..78e2833 100644 --- a/ept/debtags/vocabulary.cc +++ b/ept/debtags/vocabulary.cc @@ -18,11 +18,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <ept/debtags/vocabulary.h> -#include <ept/debtags/maint/debdbparser.h> -#include <tagcoll/input/memory.h> -#include <tagcoll/input/stdio.h> -#include <wibble/sys/fs.h> +#include "vocabulary.h" +#include "maint/debdbparser.h" +#include "ept/utils/sys.h" +#include <system_error> +#include <cassert> #include <cstring> #include <cstdio> #include <cstdlib> @@ -31,8 +31,6 @@ #include <unistd.h> using namespace std; -using namespace tagcoll; -using namespace wibble; namespace ept { namespace debtags { @@ -113,11 +111,20 @@ string Vocabulary::pathname() void Vocabulary::load(const std::string& pathname) { - if (!sys::fs::exists(pathname)) return; + if (!sys::exists(pathname)) return; // Read uncompressed data - tagcoll::input::Stdio in(pathname); - read(in); - m_timestamp = sys::fs::timestamp(pathname, 0); + FILE* in = fopen(pathname.c_str(), "rt"); + if (!in) + throw std::system_error(errno, std::system_category(), "cannot open " + pathname); + + try { + read(in, pathname); + } catch (...) { + fclose(in); + throw; + } + fclose(in); + m_timestamp = sys::timestamp(pathname, 0); } voc::TagData& voc::FacetData::obtainTag(const std::string& name) @@ -126,7 +133,7 @@ voc::TagData& voc::FacetData::obtainTag(const std::string& name) if (i == m_tags.end()) { // Create the tag if it's missing - pair<std::map<std::string, TagData>::iterator, bool> res = m_tags.insert(make_pair<std::string, TagData>(name, TagData())); + pair<std::map<std::string, TagData>::iterator, bool> res = m_tags.insert(make_pair(name, TagData())); i = res.first; i->second.name = name; } @@ -139,7 +146,7 @@ voc::FacetData& Vocabulary::obtainFacet(const std::string& name) if (i == m_facets.end()) { // Create the facet if it's missing - pair<std::map<std::string, voc::FacetData>::iterator, bool> res = m_facets.insert(make_pair<std::string, voc::FacetData>(name, voc::FacetData())); + pair<std::map<std::string, voc::FacetData>::iterator, bool> res = m_facets.insert(make_pair(name, voc::FacetData())); i = res.first; i->second.name = name; } @@ -208,9 +215,9 @@ std::set<std::string> Vocabulary::tags(const std::string& facet) const return f->tags(); } -void Vocabulary::read(tagcoll::input::Input& input) +void Vocabulary::read(FILE* input, const std::string& pathname) { - DebDBParser parser(input); + DebDBParser parser(input, pathname); DebDBParser::Record record; while (parser.nextRecord(record)) @@ -243,12 +250,11 @@ void Vocabulary::read(tagcoll::input::Input& input) if (i->first != "Tag") tag[i->first] = i->second; } - else - { - fprintf(stderr, "%s:%d: Skipping record without Tag or Facet field\n", - input.fileName().c_str(), input.lineNumber()); - } - } + else + { + fprintf(stderr, "%s: Skipping record without Tag or Facet field\n", parser.fileName().c_str()); + } + } } void Vocabulary::write() @@ -263,116 +269,68 @@ void Vocabulary::write() void Vocabulary::write(const std::string& fname) { - // Build the temp file template - char tmpfname[fname.size() + 7]; - strncpy(tmpfname, fname.c_str(), fname.size()); - strncpy(tmpfname + fname.size(), ".XXXXXX", 8); - - // Create and open the temporary file - int fd = mkstemp(tmpfname); - if (fd < 0) - throw wibble::exception::File(tmpfname, "opening file"); - - // Read the current umask - mode_t cur_umask = umask(0); - umask(cur_umask); - - // Give the file the right permissions - if (fchmod(fd, 0666 & ~cur_umask) < 0) - throw wibble::exception::File(tmpfname, "setting file permissions"); - - // Pass the file descriptor to stdio - FILE* out = fdopen(fd, "wt"); - if (!out) - throw wibble::exception::File(tmpfname, "fdopening file"); - - // Write out the merged vocabulary data - write(out); - - // Flush stdio's buffers - fflush(out); - - // Flush OS buffers - fdatasync(fd); - - // Close the file - fclose(out); - - // Rename the successfully written file to its final name - if (rename(tmpfname, fname.c_str()) == -1) - throw wibble::exception::System(string("renaming ") + tmpfname + " to " + fname); + // Serialize the merged vocabulary data + std::stringstream str; + write(str); + // Write it out atomically + sys::write_file_atomically(fname, str.str(), 0666); } -static void writeDebStyleField(FILE* out, const string& name, const string& val) throw () +static void writeDebStyleField(std::ostream& out, const string& name, const string& val) throw () { - fprintf(out, "%s: ", name.c_str()); - - // Properly escape newlines - bool was_nl = false; - for (string::const_iterator s = val.begin(); s != val.end(); s++) - if (was_nl) - // \n\n -> \n .\n - if (*s == '\n') - { - fputc(' ', out); - fputc('.', out); - fputc(*s, out); - } - // \n([^ \t]) -> \n \1 - else if (*s != ' ' && *s != '\t') - { - fputc(' ', out); - fputc(*s, out); - was_nl = false; - } - // \n[ \t] goes unchanged - else - { - fputc(*s, out); - was_nl = false; - } - else - if (*s == '\n') - { - fputc(*s, out); - was_nl = true; - } - else - fputc(*s, out); - - fputc('\n', out); + out << name << ": "; + + // Properly escape newlines + bool was_nl = false; + for (string::const_iterator s = val.begin(); s != val.end(); s++) + if (was_nl) + // \n\n -> \n .\n + if (*s == '\n') + out << " ." << *s; + // \n([^ \t]) -> \n \1 + else if (*s != ' ' && *s != '\t') + { + out << " " << *s; + was_nl = false; + } + // \n[ \t] goes unchanged + else + { + out << *s; + was_nl = false; + } + else + if (*s == '\n') + { + out << *s; + was_nl = true; + } + else + out << *s; + + out << endl; } -void Vocabulary::write(FILE* out) +void Vocabulary::write(std::ostream& out) { - long start_ofs = ftell(out); - int facetid = 0; - int tagid = 0; - - //fprintf(stderr, "Write\n"); - for (std::map<std::string, voc::FacetData>::iterator f = m_facets.begin(); f != m_facets.end(); ++f) - { - //fprintf(stderr, "Writing facet %.*s\n", PFSTR(f->first)); - writeDebStyleField(out, "Facet", f->first); - for (std::map<std::string, std::string>::const_iterator j = f->second.begin(); - j != f->second.end(); j++) - writeDebStyleField(out, j->first, j->second); - fputc('\n', out); - - for (std::map<std::string, voc::TagData>::iterator t = f->second.m_tags.begin(); - t != f->second.m_tags.end(); t++) - { - //fprintf(stderr, "Writing tag %.*s\n", PFSTR(t->first)); - writeDebStyleField(out, "Tag", t->first); - for (std::map<std::string, std::string>::const_iterator j = t->second.begin(); - j != t->second.end(); j++) - writeDebStyleField(out, j->first, j->second); - fputc('\n', out); - } - } + for (const auto& f: m_facets) + { + //fprintf(stderr, "Writing facet %.*s\n", PFSTR(f->first)); + writeDebStyleField(out, "Facet", f.first); + for (const auto& j : f.second) + writeDebStyleField(out, j.first, j.second); + out << endl; + + for (const auto& t : f.second.m_tags) + { + //fprintf(stderr, "Writing tag %.*s\n", PFSTR(t->first)); + writeDebStyleField(out, "Tag", t.first); + for (const auto& j : t.second) + writeDebStyleField(out, j.first, j.second); + out << endl; + } + } } } } - -// vim:set ts=4 sw=4: diff --git a/ept/debtags/vocabulary.h b/ept/debtags/vocabulary.h index a5a5fa3..0cc9449 100644 --- a/ept/debtags/vocabulary.h +++ b/ept/debtags/vocabulary.h @@ -28,12 +28,8 @@ #include <vector> #include <set> #include <map> - -namespace tagcoll { -namespace input { -struct Input; -} -} +#include <cstdio> +#include <iosfwd> namespace ept { namespace debtags { @@ -160,7 +156,12 @@ protected: voc::FacetData& obtainFacet(const std::string& name); voc::TagData& obtainTag(const std::string& fullname); - + + /** + * Write the vocabulary data to the given output stream + */ + void write(std::ostream& out); + public: /** * Instantiate the Debtags vocabulary @@ -229,11 +230,11 @@ public: /// Load vocabulary data from the given file void load(const std::string& pathname); - /** - * Parse and import the vocabulary from `input', merging the data with the - * previously imported ones - */ - void read(tagcoll::input::Input& input); + /** + * Parse and import the vocabulary from `input', merging the data with the + * previously imported ones + */ + void read(FILE* input, const std::string& pathname); /** * Atomically update the system vocabulary @@ -245,11 +246,6 @@ public: */ void write(const std::string& fname); - /** - * Write the vocabulary data to the given output stream - */ - void write(FILE* out); - /** * Return the default pathname for the vocabulary. * diff --git a/ept/debtags/vocabulary.test.h b/ept/debtags/vocabulary.test.h deleted file mode 100644 index 00c152c..0000000 --- a/ept/debtags/vocabulary.test.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Tag vocabulary access - * - * Copyright (C) 2003--2007 Enrico Zini <enrico@debian.org> - * - * 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 <wibble/test.h> -#include <ept/debtags/vocabulary.h> -#include <tagcoll/utils/set.h> -#include <tagcoll/input/stdio.h> -#include "ept/test.h" - -using namespace std; -using namespace tagcoll::utils; -using namespace ept::debtags; - -#define testfile TEST_ENV_DIR "debtags/vocabulary" - - -struct TestVocabulary : DebtagsTestEnvironment -{ - Test _1() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; // this will throw if it failed to load -} - - Test _2() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - assert( tags.hasFacet( "works-with" ) ); - assert( !tags.hasFacet( "blah" ) ); -} - - Test _3() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - assert( tags.hasTag( "works-with::people" ) ); - assert( !tags.hasTag( "works-with::midgets" ) ); -} - - Test _4() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - const voc::TagData *people = tags.tagData( "works-with::people" ), - *midgets = tags.tagData( "works-with::midgets" ), - *blahg = tags.tagData( "works-with::blahg" ), - *text = tags.tagData( "works-with::text" ), - *people2 = tags.tagData( "works-with::people" ); - assert( people != midgets ); - assert( people != text ); - assert( people != blahg ); - assert( midgets == blahg ); - assert( midgets == midgets ); - assert( people == people2 ); - assert( people == people ); -} - - Test _5() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - std::string a = "works-with::people", - b = "works-with::midgets"; - std::set<std::string> s = tags.tags(), - f = tags.tags( "works-with" ), - n = tags.tags( "nonsense" ); - assert( set_contains(s, a) ); - assert( set_contains(f, a) ); - assert( set_contains(s, f) ); - assert( !set_contains(s, b) ); - assert( !set_contains(f, b) ); - assert( n.empty() ); -} - - Test _6() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - const voc::FacetData* f = tags.facetData( "works-with" ); - assert(f); - assert_eq(f->name, "works-with"); - - const voc::TagData* t = tags.tagData( "works-with::people" ); - assert(t); - assert_eq(t->name, "works-with::people"); -} - - Test _7() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - const voc::FacetData* f = tags.facetData( "works-with" ); - std::set<std::string> x = tags.tags( "works-with" ); - assert( x == f->tags() ); -} - - Test _8() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - const voc::FacetData* f = tags.facetData( "does-not-work-with" ); - assert(!f); -} - - Test _9() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - const voc::FacetData* f = tags.facetData( "legacy" ); - assert(f); - assert_eq(f->shortDescription(), ""); - assert_eq(f->longDescription(), ""); - //assert_eq(f.shortDescription( "weehee" ), "weehee"); -} - - Test _10() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - // assert that one-character tag names are parsed correctly - assert( tags.hasTag( "implemented-in::c" ) ); -} - - Test _11() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - // assert that all facets are somehow working - std::set<std::string> facets = tags.facets(); - - for (std::set<std::string>::const_iterator i = facets.begin(); - i != facets.end(); i++) - { - const voc::FacetData* f = tags.facetData(*i); - assert(f); - } -} - - Test _12() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary voc; - - // assert that all tags are somehow working - std::set<std::string> tags = voc.tags(); - for (std::set<std::string>::const_iterator i = tags.begin(); - i != tags.end(); i++) - { - const voc::TagData* t = voc.tagData(*i); - assert(t); - } -} - -// Check for correctness of the first and last tag in the vocabulary - Test _13() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - const voc::TagData* first = tags.tagData("accessibility::TODO"); - assert(first); - assert_eq(first->name, string("accessibility::TODO")); - assert_eq(first->shortDescription(), string("Need an extra tag")); - - const voc::TagData* last = tags.tagData("x11::xserver"); - assert(last); - assert_eq(last->name, string("x11::xserver")); - assert_eq(last->shortDescription(), string("X Server")); -} - - Test _14() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", testfile); - Vocabulary tags; - - // check that we're seeing all the tags for a facet - std::set<std::string> t = tags.tags("accessibility"); - assert_eq(t.size(), 10u); - - t = tags.tags("works-with-format"); - assert_eq(t.size(), 33u); -} - -// If there is no data, Vocabulary should work as an empty vocabulary - Test _15() -{ - EnvOverride eo("DEBTAGS_VOCABULARY", "./empty/novocabularyhere"); - Vocabulary empty; - - assert(!empty.hasData()); - - set<std::string> facets = empty.facets(); - assert_eq(facets.size(), 0u); - - set<std::string> tags = empty.tags(); - assert_eq(tags.size(), 0u); -} - -}; diff --git a/ept/libept.pc.in b/ept/libept.pc.in index efadfce..265f032 100644 --- a/ept/libept.pc.in +++ b/ept/libept.pc.in @@ -6,6 +6,5 @@ includedir=@includedir@ Name: libept Description: Access various data sources about software packages Version: @EPT_VERSION@ -Requires: libtagcoll2 libwibble Cflags: -I${includedir} Libs: -L${libdir} -lept -lapt-pkg -lxapian -lz diff --git a/ept/popcon/local.cc b/ept/popcon/local.cc deleted file mode 100644 index 308a9e4..0000000 --- a/ept/popcon/local.cc +++ /dev/null @@ -1,168 +0,0 @@ -/** @file - * @author Enrico Zini <enrico@enricozini.org> - * Correlate popcon data with local popcon information - */ - -/* - * Copyright (C) 2007 Enrico Zini <enrico@debian.org> - * - * 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 <ept/popcon/local.h> -#include <ept/popcon/popcon.h> -#include <ept/popcon/maint/path.h> - -#include <wibble/exception.h> - -#include <algorithm> -#include <fstream> -#include <cmath> - -//#include <iostream> - -using namespace std; - -namespace ept { -namespace popcon { - -// Split a string where there are separators -static vector<string> split(const std::string& str, char sep = ' ') -{ - vector<string> res; - size_t start = 0; - while (start < str.size()) - { - size_t end = str.find(sep, start); - if (end == string::npos) - { - res.push_back(str.substr(start)); - break; - } - else - { - res.push_back(str.substr(start, end-start)); - start = end + 1; - } - } - return res; -} - -// Reverse sort pairs by comparing their second element -struct secondsort -{ - bool operator()(const pair<string, float>& a, const pair<string, float>& b) const - { - if (a.second == b.second) - return a.first > b.first; - else - return a.second > b.second; - } -}; - -Local::Local(const std::string& file) -{ - m_timestamp = Path::timestamp(file); - if (m_timestamp == 0) - return; - - ifstream in; - in.open(file.c_str()); - if (!in.good()) - throw wibble::exception::File(file, "opening file for reading"); - - while (!in.eof()) - { - std::string line; - getline(in, line); - if (line.substr(0, 10) == "POPULARITY") - continue; - if (line.substr(0, 14) == "END-POPULARITY") - continue; - vector<string> data = split(line); - if (data.size() < 4) - continue; - if (data[3] == "<NOFILES>") - // This is an empty / virtual package - m_scores.insert(make_pair(data[2], 0.1)); - else if (data.size() == 4) - // Package normally in use - m_scores.insert(make_pair(data[2], 1.0)); - else if (data[4] == "<OLD>") - // Unused packages - m_scores.insert(make_pair(data[2], 0.3)); - else if (data[4] == "<RECENT-CTIME>") - // Recently installed packages - m_scores.insert(make_pair(data[2], 0.5)); - } -} - -float Local::score(const std::string& pkg) const -{ - std::map<std::string, float>::const_iterator i = m_scores.find(pkg); - if (i == m_scores.end()) - return 0; - else - return i->second; -} - -/** - * Return the TFIDF score of the package computed against the popcon - * information. - */ -float Local::tfidf(const Popcon& popcon, const std::string& pkg) const -{ - float popconScore = popcon.score(pkg); - //cerr << pkg << ": " << score(pkg) << " * log(" << (float)popcon.submissions() << " / " << popconScore << ") = " << score(pkg) * log((float)popcon.submissions() / popconScore) << endl; - if (popconScore == 0) - return 0; - else - return score(pkg) * log((float)popcon.submissions() / popconScore); - -} - -std::vector< std::pair<std::string, float> > Local::scores() const -{ - vector< pair<string, float> > res; - // Copy the scores in res - copy(m_scores.begin(), m_scores.end(), back_inserter(res)); - // Sort res by score - sort(res.begin(), res.end(), secondsort()); - return res; -} - -std::vector< std::pair<std::string, float> > Local::tfidf(const Popcon& popcon) const -{ - vector< pair<string, float> > res; - // Compute the tfidf scores and store them into res - for (std::map<std::string, float>::const_iterator i = m_scores.begin(); - i != m_scores.end(); ++i) - { - float popconScore = popcon.score(i->first); - if (popconScore == 0) - res.push_back(make_pair(i->first, 0.0f)); - else - res.push_back(make_pair(i->first, - i->second * log((float)popcon.submissions() / popconScore))); - } - // Sort res by score - sort(res.begin(), res.end(), secondsort()); - return res; -} - -} -} - -// vim:set ts=4 sw=4: diff --git a/ept/popcon/local.h b/ept/popcon/local.h deleted file mode 100644 index 257cc72..0000000 --- a/ept/popcon/local.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef EPT_POPCON_LOCAL_H -#define EPT_POPCON_LOCAL_H - -/** @file - * @author Enrico Zini <enrico@enricozini.org> - * Correlate popcon data with local popcon information - */ - -/* - * Copyright (C) 2007 Enrico Zini <enrico@debian.org> - * - * 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 <string> -#include <vector> -#include <map> - -namespace ept { -namespace popcon { - -class Popcon; - -/** - * Access the results of the local daily popcon scan. - */ -class Local -{ -protected: - std::map<std::string, float> m_scores; - time_t m_timestamp; - -public: - Local(const std::string& file = std::string("/var/log/popularity-contest")); - - /// Get the timestamp of the local popcon information - time_t timestamp() const { return m_timestamp; } - - /// Return true if this data source has data, false if it's empty - bool hasData() const { return m_timestamp != 0; } - - /** - * Return the local score of the package - */ - float score(const std::string& pkg) const; - - /** - * Return the TFIDF score of the package computed against the popcon - * information. - * - * The TFIDF score is high when a package is representative of this system, - * that is, it is used in this system and not much used in other systems. - */ - float tfidf(const Popcon& popcon, const std::string& pkg) const; - - /** - * Read the local popcon vote and return the list of packages and their - * local scores, sorted by ascending score. - */ - std::vector< std::pair<std::string, float> > scores() const; - - /** - * Read the local popcon vote and return the list of packages and their - * TFIDF scores computed against the popcon information. - * - * The packages will be sorted by ascending score. - */ - std::vector< std::pair<std::string, float> > tfidf(const Popcon& popcon) const; -}; - -} -} - -// vim:set ts=4 sw=4: -#endif diff --git a/ept/popcon/local.test.h b/ept/popcon/local.test.h deleted file mode 100644 index 66d9919..0000000 --- a/ept/popcon/local.test.h +++ /dev/null @@ -1,111 +0,0 @@ -// -*- mode: c++; tab-width: 4; indent-tabs-mode: t -*- -/* - * popcon/local test - * - * Copyright (C) 2007 Enrico Zini <enrico@debian.org> - * - * 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 <ept/popcon/local.h> -#include <ept/popcon/popcon.h> -#include <ept/popcon/maint/path.h> - -#include <ept/test.h> - -using namespace std; -using namespace ept; -using namespace ept::popcon; - -struct TestPopconLocal -{ - Path::OverridePopconSourceDir odsd; - Path::OverridePopconIndexDir odid; - Path::OverridePopconUserSourceDir odusd; - Path::OverridePopconUserIndexDir oduid; - - Popcon popcon; - Local local; - - TestPopconLocal() - : odsd( TEST_ENV_DIR "popcon" ), - odid( TEST_ENV_DIR "popcon" ), - odusd( TEST_ENV_DIR "popcon" ), - oduid( TEST_ENV_DIR "popcon" ), - local( TEST_ENV_DIR "popcon/popularity-contest" ) - {} - - // Very basic access - Test basicAccess() - { - assert(local.score("apt") > 0); - assert(local.tfidf(popcon, "apt") > 0); - } - -#if 0 // mornfall: apparently left out by enrico, leaving as it is -// Check that every valid index is accessible -template<> template<> -void to::test< 2 >() -{ - for (size_t i = 0; i < popcon.size(); ++i) - { - //cerr << popcon.name(i) << " " << popcon.score(i) << endl; - assert(popcon.score(i) > 0); - } -} - -// Check that we can get a score for every package -template<> template<> -void to::test< 3 >() -{ - int has = 0; - for (Apt::iterator i = apt.begin(); i != apt.end(); ++i) - { - float score = popcon.score(*i); - if (score > 0) - ++has; - } - // At least 1000 packages should have a score - assert(has > 1000); -} - -// Check that scores are meaningful -template<> template<> -void to::test< 4 >() -{ - assert(popcon["apt"] > popcon["libapt-pkg-dev"]); -} - -// If there is no data, Popcon should work as if all scores were 0 -template<> template<> -void to::test<5>() -{ - Path::OverridePopconSourceDir odsd("./empty"); - Path::OverridePopconIndexDir odid("./empty"); - Path::OverridePopconUserSourceDir odusd("./empty"); - Path::OverridePopconUserIndexDir oduid("./empty"); - Popcon empty; - - assert_eq(empty.timestamp(), 0); - assert(!empty.hasData()); - - assert(empty.size() == 0); - assert(empty.score("apt") == 0.0); -} -#endif - -}; - -// vim:set ts=4 sw=4: diff --git a/ept/popcon/maint/path.cc b/ept/popcon/maint/path.cc deleted file mode 100644 index aef6314..0000000 --- a/ept/popcon/maint/path.cc +++ /dev/null @@ -1,113 +0,0 @@ -// -*- mode: c++; indent-tabs-mode: t -*- - -/** \file - * popcon paths - */ - -/* - * Copyright (C) 2005,2006,2007 Enrico Zini <enrico@debian.org>, Peter Rockai <me@mornfall.net> - * - * 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 <ept/config.h> -#include <ept/popcon/maint/path.h> - -#include <wibble/sys/fs.h> -#include <wibble/string.h> - -#include <sys/types.h> // getpwuid, getuid -#include <pwd.h> // getpwuid -#include <unistd.h> // getuid - -using namespace wibble; - -namespace ept { -namespace popcon { - -static std::string userdir() -{ - std::string rcdir; - - struct passwd* udata = getpwuid(getuid()); - rcdir = str::joinpath(udata->pw_dir, ".popcon"); - - return rcdir; -} - - -Path &Path::instance() { - if (!s_instance) { - s_instance = new Path; - instance().m_popconSourceDir = POPCON_DB_DIR; - instance().m_popconIndexDir = POPCON_DB_DIR; - instance().m_popconUserSourceDir = userdir(); - instance().m_popconUserIndexDir = userdir(); - } - return *s_instance; -} - -int Path::access( const std::string &s, int m ) { - return ::access( s.c_str(), m ); -} - -time_t Path::timestamp( const std::string& file ) { - return sys::fs::timestamp(file, 0); -} - -void Path::setPopconSourceDir( const std::string &s ) -{ - instance().m_popconSourceDir = s; -} -void Path::setPopconIndexDir( const std::string &s ) -{ - instance().m_popconIndexDir = s; -} -void Path::setPopconUserSourceDir( const std::string &s ) -{ - instance().m_popconUserSourceDir = s; -} -void Path::setPopconUserIndexDir( const std::string &s ) -{ - instance().m_popconUserIndexDir = s; -} - -std::string Path::popconSourceDir() { return instance().m_popconSourceDir; } -std::string Path::popconIndexDir() { return instance().m_popconIndexDir; } -std::string Path::popconUserSourceDir() { return instance().m_popconUserSourceDir; } -std::string Path::popconUserIndexDir() { return instance().m_popconUserIndexDir; } - -std::string Path::scores() { - return str::joinpath(popconIndexDir(), "scores"); -} - -std::string Path::scoresIndex() { - return str::joinpath(popconIndexDir(), "scores.idx"); -} - -std::string Path::userScores() { - return str::joinpath(popconUserIndexDir(), "scores"); -} - -std::string Path::userScoresIndex() { - return str::joinpath(popconUserIndexDir(), "scores.idx"); -} - -Path *Path::s_instance = 0; - -} -} - -// vim:set ts=4 sw=4: diff --git a/ept/popcon/maint/path.h b/ept/popcon/maint/path.h deleted file mode 100644 index cb4c31d..0000000 --- a/ept/popcon/maint/path.h +++ /dev/null @@ -1,125 +0,0 @@ -// -*- mode: c++; indent-tabs-mode: t -*- -/** \file - * popcon paths - */ - -/* - * Copyright (C) 2005,2006,2007 Enrico Zini <enrico@debian.org> - * - * 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 - */ - -#ifndef EPT_POPCON_PATH_H -#define EPT_POPCON_PATH_H - -#include <string> - -namespace ept { -namespace popcon { - -/** - * Singleton class to configure and access the various Popcon paths - */ -class Path -{ -public: - static std::string scores(); - static std::string scoresIndex(); - static std::string userScores(); - static std::string userScoresIndex(); - - static std::string popconSourceDir(); - static std::string popconIndexDir(); - static std::string popconUserSourceDir(); - static std::string popconUserIndexDir(); - - // Directory where Popcon source data is found - static void setPopconSourceDir( const std::string &s ); - - // Directory where Popcon indexes are kept - static void setPopconIndexDir( const std::string &s ); - - // User-specific directory for Popcon source data - static void setPopconUserSourceDir( const std::string &s ); - - // User-specific directory for Popcon index data - static void setPopconUserIndexDir( const std::string &s ); - - static int access( const std::string &, int ); - static time_t timestamp( const std::string& ); - - // RAII-style classes to temporarily override directories - class OverridePopconSourceDir - { - std::string old; - public: - OverridePopconSourceDir(const std::string& path) : old(Path::popconSourceDir()) - { - Path::setPopconSourceDir(path); - } - ~OverridePopconSourceDir() { Path::setPopconSourceDir(old); } - }; - class OverridePopconIndexDir - { - std::string old; - public: - OverridePopconIndexDir(const std::string& path) : old(Path::popconIndexDir()) - { - Path::setPopconIndexDir(path); - } - ~OverridePopconIndexDir() { Path::setPopconIndexDir(old); } - }; - class OverridePopconUserSourceDir - { - std::string old; - public: - OverridePopconUserSourceDir(const std::string& path) : old(Path::popconUserSourceDir()) - { - Path::setPopconUserSourceDir(path); - } - ~OverridePopconUserSourceDir() { Path::setPopconUserSourceDir(old); } - }; - class OverridePopconUserIndexDir - { - std::string old; - public: - OverridePopconUserIndexDir(const std::string& path) : old(Path::popconUserIndexDir()) - { - Path::setPopconUserIndexDir(path); - } - ~OverridePopconUserIndexDir() { Path::setPopconUserIndexDir(old); } - }; -protected: - static Path *s_instance; - static Path &instance(); - - // Directory where Popcon source data is found - std::string m_popconSourceDir; - - // Directory where Popcon indexes are kept - std::string m_popconIndexDir; - - // User-specific directory for Popcon source data - std::string m_popconUserSourceDir; - - // User-specific directory for Popcon index data - std::string m_popconUserIndexDir; -}; - -} -} - -// vim:set ts=4 sw=4: -#endif diff --git a/ept/popcon/maint/popconindexer.cc b/ept/popcon/maint/popconindexer.cc deleted file mode 100644 index a8dea69..0000000 --- a/ept/popcon/maint/popconindexer.cc +++ /dev/null @@ -1,248 +0,0 @@ -#include <ept/popcon/popcon.h> -#include <ept/popcon/maint/popconindexer.h> -#include <ept/popcon/maint/path.h> - -#include <wibble/exception.h> -#include <wibble/sys/fs.h> - -#include <tagcoll/diskindex/mmap.h> - -#include <unistd.h> -#include <set> -#include <string> -#include <cstdio> -#include <cstring> - -using namespace std; - -namespace ept { -namespace popcon { - -template<typename STRUCT> -struct StructIndexer : public tagcoll::diskindex::MMapIndexer -{ - const STRUCT& data; - StructIndexer(const STRUCT& data) : data(data) {} - - int encodedSize() const { return sizeof(STRUCT); } - void encode(char* buf) const { *(STRUCT*)buf = data; } -}; - -/// MMapIndexer that indexes the package names -struct PopconGenerator : public tagcoll::diskindex::MMapIndexer -{ - // Sorted set of all available package names and data - std::map<std::string, Score> data; - - int encodedSize() const - { - int size = data.size() * sizeof(Score); - for (std::map<std::string, Score>::const_iterator i = data.begin(); - i != data.end(); ++i) - size += i->first.size() + 1; - return tagcoll::diskindex::MMap::align(size); - } - - void encode(char* buf) const - { - int pos = data.size() * sizeof(Score); - int idx = 0; - for (std::map<std::string, Score>::const_iterator i = data.begin(); - i != data.end(); ++i) - { - ((Score*)buf)[idx] = i->second; - ((Score*)buf)[idx].offset = pos; - memcpy(buf + pos, i->first.c_str(), i->first.size() + 1); - pos += i->first.size() + 1; - ++idx; - } - } -}; - - -PopconIndexer::PopconIndexer() - : mainSource(Path::popconSourceDir()), - userSource(Path::popconUserSourceDir()) -{ - rescan(); -} - -void PopconIndexer::rescan() -{ - ts_main_src = mainSource.timestamp(); - ts_user_src = userSource.timestamp(); - ts_main_sco = Path::timestamp(Path::scores()); - ts_user_sco = Path::timestamp(Path::userScores()); - ts_main_idx = Path::timestamp(Path::scoresIndex()); - ts_user_idx = Path::timestamp(Path::userScoresIndex()); -} - -bool PopconIndexer::needsRebuild() const -{ - // If there are no indexes of any kind, then we need rebuilding - if (ts_user_sco == 0 || ts_main_sco == 0 || ts_user_idx == 0 && ts_main_idx == 0) - return true; - - // If the user index is ok, then we are fine - if (ts_user_sco >= sourceTimestamp() && ts_user_idx >= sourceTimestamp()) - return false; - - // If there are user sources, then we cannot use the system index - if (ts_user_src > 0) - return true; - - // If there are no user sources, then we can fallback on the system - // indexes in case the user indexes are not up to date - if (ts_main_sco >= sourceTimestamp() && ts_main_idx >= sourceTimestamp()) - return false; - - return true; -} - -bool PopconIndexer::userIndexIsRedundant() const -{ - // If there is no user index, then it is not redundant - if (ts_user_idx == 0) - return false; - - // If the system index is not up to date, then the user index is not - // redundant - if (ts_main_idx < sourceTimestamp()) - return false; - - return true; -} - -bool PopconIndexer::rebuild(const std::string& scofname, const std::string& idxfname) -{ - PopconGenerator gen; - InfoStruct is; - is.submissions = 0; - if (!mainSource.readScores(gen.data, is.submissions)) - userSource.readScores(gen.data, is.submissions); - if (gen.data.empty()) - return false; - - StructIndexer<InfoStruct> infoStruct(is); - - // Create the index - tagcoll::diskindex::MasterMMapIndexer master(idxfname); - master.append(gen); - master.append(infoStruct); - master.commit(); - -// for (map<string, Score>::const_iterator i = gen.data.begin(); i != gen.data.end(); ++i) -// { -// fprintf(stderr, "%s %d %f\n", i->first.c_str(), i->second.offset, i->second.score); -// } - - // Create the score file - FILE* out = fopen(scofname.c_str(), "wt"); - if (out == NULL) - throw wibble::exception::File(scofname, "opening and truncating file for writing"); - for (map<string, Score>::const_iterator i = gen.data.begin(); - i != gen.data.end(); ++i) - { - fprintf(out, "%s %f\n", i->first.c_str(), i->second.score); - } - fclose(out); - return true; -} - -bool PopconIndexer::rebuildIfNeeded() -{ - if (needsRebuild()) - { - // Decide if we rebuild the user index or the system index - if (Path::access(Path::popconIndexDir(), W_OK) == 0) - { - // Since we can write on the system index directory, we rebuild - // the system index - if (!rebuild(Path::scores(), Path::scoresIndex())) - return false; - ts_main_sco = Path::timestamp(Path::scores()); - ts_main_idx = Path::timestamp(Path::scoresIndex()); - if (Path::scores() == Path::userScores()) - ts_user_sco = ts_main_sco; - if (Path::scoresIndex() == Path::userScoresIndex()) - ts_user_idx = ts_main_idx; - } else { - wibble::sys::fs::mkFilePath(Path::userScores()); - wibble::sys::fs::mkFilePath(Path::userScoresIndex()); - if (!rebuild(Path::userScores(), Path::userScoresIndex())) - return false; - ts_user_sco = Path::timestamp(Path::userScores()); - ts_user_idx = Path::timestamp(Path::userScoresIndex()); - } - return true; - } - return false; -} - -bool PopconIndexer::deleteRedundantUserIndex() -{ - if (userIndexIsRedundant()) - { - // Delete the user indexes if they exist - if (Path::scores() != Path::userScores()) - { - unlink(Path::userScores().c_str()); - ts_user_sco = 0; - } - if (Path::scoresIndex() != Path::userScoresIndex()) - { - unlink(Path::userScoresIndex().c_str()); - ts_user_idx = 0; - } - return true; - } - return false; -} - -bool PopconIndexer::getUpToDatePopcon(std::string& scofname, std::string& idxfname) -{ - // If there are no indexes of any kind, then we have nothing to return - if (ts_user_sco == 0 && ts_main_sco == 0 && ts_user_idx == 0 && ts_main_idx == 0) - return false; - - // If the user index is up to date, use it - if (ts_user_sco >= sourceTimestamp() && - ts_user_idx >= sourceTimestamp()) - { - scofname = Path::userScores(); - idxfname = Path::userScoresIndex(); - return true; - } - - // If the user index is not up to date and we have user sources, we cannot - // fall back to the system index - if (ts_user_src != 0) - return false; - - // Fallback to the system index - if (ts_main_sco >= sourceTimestamp() && - ts_main_idx >= sourceTimestamp()) - { - scofname = Path::scores(); - idxfname = Path::scoresIndex(); - return true; - } - - return false; -} - - -bool PopconIndexer::obtainWorkingPopcon(std::string& scofname, std::string& idxfname) -{ - PopconIndexer indexer; - - indexer.rebuildIfNeeded(); - indexer.deleteRedundantUserIndex(); - return indexer.getUpToDatePopcon(scofname, idxfname); -} - - -} -} - -// vim:set ts=4 sw=4: diff --git a/ept/popcon/maint/popconindexer.h b/ept/popcon/maint/popconindexer.h deleted file mode 100644 index 86789cf..0000000 --- a/ept/popcon/maint/popconindexer.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef EPT_DEBTAGS_PKGIDINDEXER_H -#define EPT_DEBTAGS_PKGIDINDEXER_H - -/** @file - * @author Enrico Zini <enrico@enricozini.org> - * Rebuild and maintain the map from package IDs to package names - */ - -/* - * Copyright (C) 2003-2007 Enrico Zini <enrico@debian.org> - * - * 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 <ept/popcon/maint/sourcedir.h> -#include <string> - -namespace ept { -namespace popcon { - -struct InfoStruct -{ - size_t submissions; -}; - -struct PopconIndexer -{ - SourceDir mainSource; - SourceDir userSource; - time_t ts_main_src; - time_t ts_user_src; - time_t ts_main_sco; - time_t ts_user_sco; - time_t ts_main_idx; - time_t ts_user_idx; - - time_t sourceTimestamp() const - { - time_t res = ts_main_src; - if (ts_user_src > res) res = ts_user_src; - return res; - - } - bool needsRebuild() const; - bool rebuild(const std::string& scofname, const std::string& idxfname); - bool rebuildIfNeeded(); - bool getUpToDatePopcon(std::string& scofname, std::string& idxfname); - - bool userIndexIsRedundant() const; - bool deleteRedundantUserIndex(); - - void rescan(); - - PopconIndexer(); - - static bool obtainWorkingPopcon(std::string& scofname, std::string& idxfname); -}; - -} -} - -// vim:set ts=4 sw=4: -#endif diff --git a/ept/popcon/maint/sourcedir.cc b/ept/popcon/maint/sourcedir.cc deleted file mode 100644 index c908edd..0000000 --- a/ept/popcon/maint/sourcedir.cc +++ /dev/null @@ -1,155 +0,0 @@ -#include <ept/popcon/maint/sourcedir.h> -#include <ept/popcon/maint/path.h> - -#include <wibble/string.h> -#include <wibble/sys/fs.h> - -#include <tagcoll/input/zlib.h> -#include <tagcoll/input/stdio.h> - -#include <cstdlib> - -using namespace std; -using namespace wibble; - -namespace ept { -namespace popcon { - -SourceDir::SourceDir(const std::string& path) - : sys::fs::Directory(path) -{ -} -SourceDir::~SourceDir() -{ -} - -SourceDir::FileType SourceDir::fileType(const std::string& name) -{ - if (name[0] == '.') return SKIP; - - if (name == "all-popcon-results.txt") return RAW; - if (name == "all-popcon-results.txt.gz") return RAWGZ; - - return SKIP; -} - -time_t SourceDir::timestamp() -{ - if (!exists()) return 0; - - time_t max = 0; - for (const_iterator d = begin(); d != end(); ++d) - { - string name = *d; - FileType type = fileType(name); - if (type == SKIP) continue; - - time_t ts = Path::timestamp(str::joinpath(m_path, name)); - if (ts > max) max = ts; - } - - return max; -} - -bool readLine(tagcoll::input::Input& in, string& str) -{ - str.clear(); - int c; - while ((c = in.nextChar()) != tagcoll::input::Input::Eof && c != '\n') - str += c; - return c != tagcoll::input::Input::Eof; -} - -static void parseScores(tagcoll::input::Input& in, map<std::string, Score>& out, size_t& submissions) -{ - string line; - while (readLine(in, line)) - { - if (line.size() < 10) - continue; - if (line.substr(0, 13) == "Submissions: ") - { - submissions = strtoul(line.substr(13).c_str(), 0, 10); - continue; - } - if (line.substr(0, 9) != "Package: ") - continue; - size_t start = 9; - size_t end = line.find(' ', start); - if (end == string::npos) - continue; - string name = line.substr(start, end-start); - // Skip packages not in the apt index - //if (!apt.isValid(name)) - //continue; - - start = line.find_first_not_of(' ', end); - if (start == string::npos) continue; - end = line.find(' ', start); - if (end == string::npos) continue; - string vote = line.substr(start, end-start); - - start = line.find_first_not_of(' ', end); - if (start == string::npos) continue; - end = line.find(' ', start); - if (end == string::npos) continue; - string old = line.substr(start, end-start); - - start = line.find_first_not_of(' ', end); - if (start == string::npos) continue; - end = line.find(' ', start); - if (end == string::npos) continue; - string recent = line.substr(start, end-start); - - start = line.find_first_not_of(' ', end); - if (start == string::npos) continue; - end = line.find(' ', start); - if (end == string::npos) end = line.size(); - string nofiles = line.substr(start, end-start); - - float score = (float)strtoul(vote.c_str(), NULL, 10) - + (float)strtoul(recent.c_str(), NULL, 10) * 0.5f - + (float)strtoul(old.c_str(), NULL, 10) * 0.3f - + (float)strtoul(nofiles.c_str(), NULL, 10) * 0.8f; - - if (score > 0) - out.insert(make_pair(name, Score(score))); - } -} - -bool SourceDir::readScores(map<std::string, Score>& out, size_t& submissions) -{ - if (!exists()) return false; - - bool done = false; - - for (const_iterator d = begin(); d != end(); ++d) - { - string name = *d; - FileType type = fileType(name); - if (type == RAW) - { - // Read uncompressed data - tagcoll::input::Stdio in(str::joinpath(m_path, name)); - - // Read the scores - parseScores(in, out, submissions); - done = true; - } - else if (type == RAWGZ) - { - // Read compressed data - tagcoll::input::Zlib in(str::joinpath(m_path, name)); - - // Read the scores - parseScores(in, out, submissions); - done = true; - } - } - return done; -} - -} -} - -// vim:set ts=4 sw=4: diff --git a/ept/popcon/maint/sourcedir.h b/ept/popcon/maint/sourcedir.h deleted file mode 100644 index eccb6f1..0000000 --- a/ept/popcon/maint/sourcedir.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef EPT_POPCON_SOURCEDIR_H -#define EPT_POPCON_SOURCEDIR_H - -/** @file - * @author Enrico Zini <enrico@enricozini.org> - * Popcon data source directory access - */ - -/* - * Copyright (C) 2003,2004,2005,2006,2007 Enrico Zini <enrico@debian.org> - * - * 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 <ept/popcon/popcon.h> -#include <wibble/sys/fs.h> -#include <string> -#include <map> - -namespace ept { -namespace popcon { - -/** - * Access a directory containing Debtags data files - */ -class SourceDir : public wibble::sys::fs::Directory -{ -protected: - enum FileType { SKIP, RAW, RAWGZ }; - - // Check if a file name is a tag file, a vocabulary file or a file to skip. - // Please notice that it works on file names, not paths. - FileType fileType(const std::string& name); - -public: - SourceDir(const std::string& path); - ~SourceDir(); - - /// Return the time of the newest file in the source directory - time_t timestamp(); - - /** - * Read the tag files in the directory and output their content to the map - */ - bool readScores(std::map<std::string, Score>& out, size_t& submissions); -}; - -} -} - -// vim:set ts=4 sw=4: -#endif diff --git a/ept/popcon/popcon.cc b/ept/popcon/popcon.cc deleted file mode 100644 index 2ce9e53..0000000 --- a/ept/popcon/popcon.cc +++ /dev/null @@ -1,95 +0,0 @@ -// -*- mode: c++; tab-width: 4; indent-tabs-mode: t -*- - -/** @file - * @author Enrico Zini <enrico@enricozini.org> - * Quick map from package IDs to package names - */ - -/* - * Copyright (C) 2007 Enrico Zini <enrico@debian.org> - * - * 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 <ept/popcon/popcon.h> -#include <ept/popcon/maint/popconindexer.h> -#include <ept/popcon/maint/path.h> - -//#include <iostream> - -using namespace std; - -namespace ept { -namespace popcon { - -size_t Popcon::GeneralInfo::submissions() const -{ - if (!m_buf) return 0; - return ((InfoStruct*)m_buf)->submissions; -} - -Popcon::Popcon() -{ - std::string scofname, idxfname; - - if (!PopconIndexer::obtainWorkingPopcon(scofname, idxfname)) - { - m_timestamp = 0; - return; - } - - //cerr << "GOT " << scofname << " " << idxfname << endl; - - m_timestamp = Path::timestamp(idxfname); - - mastermmap.init(idxfname); - tagcoll::diskindex::MMap::init(mastermmap, 0); - - m_info.init(mastermmap, 1); - - //cerr << "SIZE " << size() << endl; - //for (size_t i = 0; i < size(); ++i) - //{ - // cerr << "EL " << i << ": " << ((Score*)m_buf)[i].offset << " " << ((Score*)m_buf)[i].score << endl; - //} -} - -float Popcon::scoreByName(const std::string& name) const -{ - // Binary search the index to find the package ID - int begin, end; - - /* Binary search */ - begin = -1, end = size(); - while (end - begin > 1) - { - int cur = (end + begin) / 2; - if (this->name(cur) > name) - end = cur; - else - begin = cur; - } - - if (begin == -1 || this->name(begin) != name) - //throw NotFoundException(string("looking for the ID of string ") + str); - return 0; - else - return score(begin); -} - -} -} - -// vim:set ts=4 sw=4: diff --git a/ept/popcon/popcon.h b/ept/popcon/popcon.h deleted file mode 100644 index 684f98a..0000000 --- a/ept/popcon/popcon.h +++ /dev/null @@ -1,153 +0,0 @@ -// -*- mode: c++; tab-width: 4; indent-tabs-mode: t -*- -#ifndef EPT_POPCON_POPCON_H -#define EPT_POPCON_POPCON_H - -/** @file - * @author Enrico Zini <enrico@enricozini.org> - * Access popcon data - */ - -/* - * Copyright (C) 2007 Enrico Zini <enrico@debian.org> - * - * 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 <tagcoll/diskindex/mmap.h> -#include <string> - -namespace ept { -namespace apt { -class Apt; -} - -namespace popcon { - -/** - * Store the score information in the popcon cache. - * - * Currently, this is only one float; more can be added in the future. - */ -class Score -{ -protected: - unsigned offset; - -public: - float score; - - Score(float score) : offset(offset), score(score) {} - - friend class Popcon; - friend class PopconIndexer; - friend class PopconGenerator; -}; - -/** - * Maps Packages to IDs and vice-versa. - * - * This is used in building the Debtags fast index, which works representing - * tags and packages as int IDs. - * - * Index building works like this: - * 1. The file all-popcon-results.txt.gz is downloaded from - * http://popcon.debian.org/all-popcon-results.txt.gz - * 2. The file is put in either ~/.popcon/all-popcon-results.txt.gz - * or in /var/lib/popcon/all-popcon-results.txt.gz - * 3. If the file is newer than the index, it will be automatically used to - * recompute the scores and rebuild the index. - */ -class Popcon : public tagcoll::diskindex::MMap -{ - struct GeneralInfo : public tagcoll::diskindex::MMap - { - size_t submissions() const; - }; - - tagcoll::diskindex::MasterMMap mastermmap; - time_t m_timestamp; - - GeneralInfo m_info; - - /// Get the score structure by index - const Score* structByIndex(size_t idx) const - { - if (idx >= 0 && idx < size()) - return (Score*)m_buf + idx; - return 0; - } - -public: - Popcon(); - - /// Get the timestamp of when the index was last updated - time_t timestamp() const { return m_timestamp; } - - /// Return true if this data source has data, false if it's empty - bool hasData() const { return m_timestamp != 0; } - - /// Return the total number of popcon submissions - size_t submissions() const { return m_info.submissions(); } - - /// Get the number of packages in the index - size_t size() const - { - if (m_buf) - return ((Score*)m_buf)->offset / sizeof(Score); - else - return 0; - } - - /** - * Get a package name by index - * - * If the index is not valid, returns the empty string. - */ - std::string name(size_t idx) const - { - const Score* s = structByIndex(idx); - if (s == 0) return std::string(); - return std::string(m_buf + s->offset); - } - - /// Get the score by index - float scoreByIndex(size_t idx) const - { - const Score* s = structByIndex(idx); - if (!s) return 0; - return s->score; - } - - /// Get the score structure by package name - float scoreByName(const std::string& name) const; - - /// Get the score by index - float score(size_t idx) const { return scoreByIndex(idx); } - - /// Get the score by index - float operator[](int idx) const { return scoreByIndex(idx); } - - /// Get the score by name - float score(const std::string& name) const { return scoreByName(name); } - - /// Get the score structure by package name - float operator[](const std::string& name) const { return scoreByName(name); } -}; - -} -} - -// vim:set ts=4 sw=4: -#endif diff --git a/ept/popcon/popcon.test.h b/ept/popcon/popcon.test.h deleted file mode 100644 index 4509b85..0000000 --- a/ept/popcon/popcon.test.h +++ /dev/null @@ -1,108 +0,0 @@ -// -*- mode: c++; tab-width: 4; indent-tabs-mode: t -*- -/* - * popcon test - * - * Copyright (C) 2007 Enrico Zini <enrico@debian.org> - * - * 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 <ept/popcon/popcon.h> -#include <ept/popcon/maint/path.h> -#include <ept/apt/apt.h> -#include <set> - -#include <ept/test.h> - -using namespace std; -using namespace ept; -using namespace ept::popcon; -using namespace ept::apt; - -struct TestPopcon -{ - popcon::Path::OverridePopconSourceDir odsd; - popcon::Path::OverridePopconIndexDir odid; - popcon::Path::OverridePopconUserSourceDir odusd; - popcon::Path::OverridePopconUserIndexDir oduid; - - Apt apt; - Popcon popcon; - - TestPopcon() - : odsd( TEST_ENV_DIR "popcon" ), - odid( TEST_ENV_DIR "popcon" ), - odusd( TEST_ENV_DIR "popcon" ), - oduid( TEST_ENV_DIR "popcon" ) - {} - - Test basicAccess() - { - assert_eq(popcon.submissions(), 52024); - assert(popcon.size() > 0); - assert(popcon.score(0) > 0); - assert(!popcon.name(0).empty()); - } - - // Check that every valid index is accessible - Test accessibility() - { - for (size_t i = 0; i < popcon.size(); ++i) - { - //cerr << popcon.name(i) << " " << popcon.score(i) << endl; - assert(popcon.score(i) > 0); - } - } - - // Check that we can get a score for every package - Test haveScores() - { - int has = 0; - for (Apt::iterator i = apt.begin(); i != apt.end(); ++i) - { - float score = popcon.score(*i); - if (score > 0) - ++has; - } - // At least 1000 packages should have a score - assert(has > 1000); - } - - // Check that scores are meaningful - Test validScores() - { - assert(popcon["apt"] > popcon["libapt-pkg-dev"]); - } - - // If there is no data, Popcon should work as if all scores were 0 - Test fallbackValues() - { - popcon::Path::OverridePopconSourceDir odsd("./empty"); - popcon::Path::OverridePopconIndexDir odid("./empty"); - popcon::Path::OverridePopconUserSourceDir odusd("./empty"); - popcon::Path::OverridePopconUserIndexDir oduid("./empty"); - Popcon empty; - - assert_eq(empty.timestamp(), 0); - assert(!empty.hasData()); - - assert_eq(empty.submissions(), 0); - assert(empty.size() == 0); - assert(empty.score("apt") == 0.0); - } - -}; - -// vim:set ts=4 sw=4: diff --git a/ept/sources.h b/ept/sources.h deleted file mode 100644 index cefbc56..0000000 --- a/ept/sources.h +++ /dev/null @@ -1,220 +0,0 @@ -/** -*- C++ -*- - @file ept/sources.h - @author Peter Rockai <me@mornfall.net> -*/ - -#include <iterator> -#include <iostream> -#include <sstream> -#include <string> - -#include <wibble/range.h> -#include <wibble/mixin.h> - -#ifndef EPT_SOURCES_H -#define EPT_SOURCES_H - -namespace ept { - -struct Sources { - struct Entry : wibble::mixin::Comparable< Entry > { - typedef wibble::Range< std::string > StringRange; - typedef wibble::Consumer< std::string > StringConsumer; - typedef std::vector< std::string > StringVector; - struct Word { std::string s; }; - enum Type { Binary, Source, Comment }; - Entry( bool e = false, Type t = Comment, - std::string u = "", std::string d = "", - StringRange c = wibble::range( *new StringVector ) ) - : m_enabled( e ), m_type( t ), m_url( u ), m_dist( d ) - { - c.output( wibble::consumer( m_components ) ); - } - - Entry( const Entry &e ) - : m_enabled( e.m_enabled ), m_type( e.m_type ), m_url( e.m_url ), - m_dist( e.m_dist ), m_comment( e.m_comment ) - { - wibble::range( e.m_components ).output( wibble::consumer( m_components ) ); - } - - bool operator< ( const Entry &o ) const { - if (type() < o.type()) - return true; - if (enabled() < o.enabled()) - return true; - if (url() < o.url()) - return true; - if (distribution() < o.distribution()) - return true; - if (components() < o.components()) - return true; - if (comment() < o.comment()) - return true; - return false; - } - - bool operator== ( const Entry &e ) const { - return not ( ( *this < e ) || ( e < *this ) ); - } - - std::string components() const { - std::ostringstream s; - std::copy( m_components.begin(), m_components.end(), - std::ostream_iterator< std::string >( s, " " ) ); - return s.str(); - } - - void setComponents( const std::string &s ) { - std::istringstream i( s ); - m_components.clear(); - std::copy( std::istream_iterator< std::string >( i ), - std::istream_iterator< std::string >(), - wibble::consumer( m_components ) ); - } - - std::string typeString() const { - switch (type()) - { - case Binary: return "deb"; - case Source: return "deb-src"; - case Comment: return "comment"; - } - } - - void setTypeString( const std::string &s ) { - if (s == "deb") setType( Binary ); - if (s == "deb-src") setType( Source ); - if (s == "comment") setType( Comment ); - } - - std::string distribution() const { return m_dist; } - void setDistribution( const std::string &s ) { m_dist = s; } - - std::string url() const { return m_url; } - void setUrl( const std::string &s ) { m_url = s; } - - bool enabled() const { return m_enabled; } - void setEnabled( bool e ) { m_enabled = e; } - - std::string comment() const { return m_comment; } - void setComment( const std::string &s ) { m_comment = s; } - - Type type() const { return m_type; } - void setType( Type t ) { - m_type = t; - if (t == Comment) setEnabled( false ); - } - - friend std::istream &operator >>( std::istream &i, Entry &s ); - - protected: - - bool m_enabled; - Type m_type; - std::string m_url; - std::string m_dist; - StringVector m_components; - std::string m_comment; - }; - void add( const Entry &e ) { - wibble::consumer( m_entries ).consume( e ); - } - void clear() { m_entries.clear(); } - void disable( const Entry & ); - void enable( const Entry & ); - wibble::Range< Entry > entries() const { - return wibble::range( m_entries ); - } - friend std::istream &operator >>( std::istream &i, Sources &s ); -protected: - std::vector< Entry > m_entries; -}; - -inline std::istream &operator >>( std::istream &i, Sources::Entry::Word &w ) -{ - bool bracket = false, quote = false, started = false; - char c; - w.s = ""; - while (!i.eof()) { - c = i.get(); - if (started && !quote && !bracket && isspace( c )) - break; - if (!isspace( c )) - started = true; - if (started) - w.s += c; - if (bracket && c == ']') - bracket = false; - if (quote && c == '"') - quote = false; - if (!quote && c == '[') - bracket = true; - if (!bracket && c == '"') - quote = true; - } - return i; -} - -inline std::istream &operator >>( std::istream &i, Sources::Entry &e ) { - std::string line, tmp; - std::getline( i, line ); - std::istringstream l( line ); - // std::cerr << "parsing line: " << line << std::endl; - l >> tmp; - e.setEnabled( true ); - if (tmp[0] == '#') { - if (tmp.size() > 1) - tmp = tmp.substr(1); - else - l >> tmp; - e.setEnabled( false ); - } - // std::cerr << "type: " << tmp << std::endl; - if (tmp == "deb" || tmp == "deb-src") { - e.setTypeString( tmp ); - } else { - // std::cerr << "comment: '" << line << "'" << std::endl; - e.setType( Sources::Entry::Comment ); - e.setEnabled( false ); - e.setComment( line ); - return i; - } - Sources::Entry::Word w; - l >> w; e.m_url = w.s; - l >> w; e.m_dist = w.s; - e.m_components.clear(); - std::copy( std::istream_iterator< std::string >( l ), - std::istream_iterator< std::string >(), - wibble::consumer( e.m_components ) ); - return i; -} - -inline std::ostream &operator <<( std::ostream &o, const Sources::Entry &e ) -{ - if (e.type() == Sources::Entry::Comment) - return o << e.comment(); - if (! e.enabled()) - o << "# "; - o << e.typeString(); - o << " " << e.url() << " " << e.distribution() << " " << e.components(); - return o; -} - -inline std::istream &operator >>( std::istream &i, Sources &s ) { - std::copy( std::istream_iterator< Sources::Entry >( i ), - std::istream_iterator< Sources::Entry >(), - wibble::consumer( s.m_entries ) ); - return i; -} - -inline std::ostream &operator <<( std::ostream &o, const Sources &s ) { - std::copy( s.entries().begin(), s.entries().end(), - std::ostream_iterator< Sources::Entry >( o, "\n" ) ); - return o; -} - -} - -#endif -// vim:set ts=4 sw=4: diff --git a/ept/test-data/popcon/all-popcon-results.txt.gz b/ept/test-data/popcon/all-popcon-results.txt.gz Binary files differdeleted file mode 100644 index 8b3bf6a..0000000 --- a/ept/test-data/popcon/all-popcon-results.txt.gz +++ /dev/null diff --git a/ept/test-data/popcon/popularity-contest b/ept/test-data/popcon/popularity-contest deleted file mode 100644 index 6eeec20..0000000 --- a/ept/test-data/popcon/popularity-contest +++ /dev/null @@ -1,1904 +0,0 @@ -POPULARITY-CONTEST-0 TIME:1181000000 ID:00000000000000000000000000000000 ARCH:amd64 POPCONVER:1.42 -1181048444 1181048464 powertop /usr/bin/powertop <RECENT-CTIME> -1181041113 1181041119 signing-party /usr/bin/keylookup <RECENT-CTIME> -1181041113 1181041118 postgresql-common /usr/share/postgresql-common/t/TestLib.pm <RECENT-CTIME> -1181041113 1181041114 cpio /bin/mt-gnu <RECENT-CTIME> -1181041113 1181041118 postgresql-client-common /usr/bin/pg_restore <RECENT-CTIME> -1180897461 1180897510 libgnome2-0 /usr/bin/gnome-open <RECENT-CTIME> -1180897461 1180897574 unionfs-tools /usr/sbin/unionimap <RECENT-CTIME> -1180897461 1180897545 pppoe /usr/sbin/pppoe-relay <RECENT-CTIME> -1180897461 1180897498 recordmydesktop /usr/bin/recordmydesktop <RECENT-CTIME> -1180897461 1180897480 aircrack-ng /usr/sbin/airmon-ng <RECENT-CTIME> -1180897461 1180897461 dmidecode /usr/sbin/dmidecode <RECENT-CTIME> -1180897461 1180897583 zlib-bin /usr/bin/miniunzip <RECENT-CTIME> -1180897461 1180897462 info /usr/bin/infokey <RECENT-CTIME> -1180897461 1180897480 whois /usr/bin/mkpasswd <RECENT-CTIME> -1180897461 1180897572 tofrodos /usr/bin/dos2unix <RECENT-CTIME> -1180897461 1180897479 texinfo /usr/bin/ginstall-info <RECENT-CTIME> -1180897461 1180897510 libgnome2-dev /usr/lib/libgnome-2.a <RECENT-CTIME> -1180897461 1180897484 fakeroot /usr/bin/faked-sysv <RECENT-CTIME> -1180897461 1180897479 python-central /usr/bin/dh_pycentral <RECENT-CTIME> -1180897461 1180897474 file /usr/bin/file <RECENT-CTIME> -1180897461 1180897575 wdiff /usr/bin/wdiff <RECENT-CTIME> -1180897461 1180897584 915resolution /usr/sbin/915resolution <RECENT-CTIME> -1180897461 1180897544 openvpn /usr/include/openvpn/openvpn-plugin.h <RECENT-CTIME> -1180897461 1180897486 gconf2 /usr/sbin/update-gconf-defaults <RECENT-CTIME> -1180897461 1180897505 libaudio-dev /usr/lib/libaudio.a <RECENT-CTIME> -1180897461 1180897555 python2.4 /usr/bin/pdb2.4 <RECENT-CTIME> -1180897461 1180897469 module-init-tools /sbin/lsmod <RECENT-CTIME> -1180897461 1180897539 noiz2sa-data /usr/share/games/noiz2sa/ogg/ogg.h <RECENT-CTIME> -1180897461 1180897462 iputils-ping /bin/ping6 <RECENT-CTIME> -1180897461 1180897498 gv /usr/bin/gv <RECENT-CTIME> -1180897461 1180897497 gtk-recordmydesktop /usr/bin/gtk-recordMyDesktop <RECENT-CTIME> -1180897461 1180897493 mkisofs /usr/bin/mkhybrid <RECENT-CTIME> -1180897461 1180897586 reprepro /usr/bin/changestool <RECENT-CTIME> -1180897461 1180897493 genisoimage /usr/bin/isovfy <RECENT-CTIME> -1180897461 1180897560 qemu /usr/sbin/qemu-make-debian-root <RECENT-CTIME> -1180897461 1180897575 vde2 /usr/bin/vdeqemu <RECENT-CTIME> -1180897461 1180897540 ntp /usr/bin/ntpq <RECENT-CTIME> -1180897461 1180897508 libexif-dev /usr/include/libexif/exif-content.h <RECENT-CTIME> -1180897461 1180897538 netpbm /usr/bin/bmptoppm <RECENT-CTIME> -1180897461 1180897586 simple-cdd /usr/bin/build-simple-cdd <RECENT-CTIME> -1180897461 1180897492 psmisc /usr/bin/pstree.x11 <RECENT-CTIME> -1180897461 1180897471 dictionaries-common /usr/sbin/update-default-aspell <RECENT-CTIME> -1180897461 1180897534 libungif4-dev /usr/lib/libgif.a <RECENT-CTIME> -1180897461 1180897582 xpdf-common /usr/sbin/update-xpdfrc <RECENT-CTIME> -1180897461 1180897571 testdisk /usr/sbin/photorec <RECENT-CTIME> -1180897461 1180897470 bind9-host /usr/bin/host <RECENT-CTIME> -1180897461 1180897484 eject /usr/bin/volname <RECENT-CTIME> -1180897461 1180897478 pidentd /usr/sbin/ikeygen <RECENT-CTIME> -1180897461 1180897556 python-minimal /usr/bin/python <RECENT-CTIME> -1180897461 1180897586 xgsmlib /usr/bin/xgsm <RECENT-CTIME> -1180897461 1180897585 libccid /usr/bin/RSA_SecurID_getpasswd <RECENT-CTIME> -1180897461 1180897500 hal /usr/sbin/hald <RECENT-CTIME> -1180897461 1180897570 samba-common /usr/bin/testparm <RECENT-CTIME> -1180897461 1180897499 usbutils /usr/bin/lsusb <RECENT-CTIME> -1180897461 1180897566 smbclient /usr/bin/smbcquotas <RECENT-CTIME> -1180897461 1180897555 python /usr/bin/pdb <RECENT-CTIME> -1180897461 1180897581 xpdf-reader /usr/bin/xpdf.bin <RECENT-CTIME> -1180897461 1180897475 ispell /usr/sbin/ispellconfig <RECENT-CTIME> -1180897461 1180897575 webcpp /usr/bin/scs2scs2 <RECENT-CTIME> -1180897461 1180897482 daemon /usr/bin/daemon <RECENT-CTIME> -1180897461 1180897482 cdparanoia /usr/bin/cdparanoia <RECENT-CTIME> -1180897461 1180897485 libgconf2-dev /usr/lib/libgconf-2.a <RECENT-CTIME> -1180897461 1180897549 python-xml /usr/bin/xmlproc_val.python-xml <RECENT-CTIME> -1180897461 1180897481 blktrace /usr/sbin/blktrace <RECENT-CTIME> -1180897461 1180897584 hfsutils /usr/bin/humount <RECENT-CTIME> -1180897461 1180897541 openssl /usr/bin/openssl <RECENT-CTIME> -1180897461 1180897503 libart-2.0-dev /usr/bin/libart2-config <RECENT-CTIME> -1180897461 1180897579 xmms /usr/bin/wmxmms <RECENT-CTIME> -1180897461 1180897560 ruby /usr/bin/ruby <RECENT-CTIME> -1180897461 1180897570 sdparm /usr/bin/sdparm <RECENT-CTIME> -1180897461 1180897585 ogdi-bin /usr/bin/ogdi_info <RECENT-CTIME> -1180897461 1180897569 smbfs /sbin/mount.smb <RECENT-CTIME> -1180897461 1180897483 libglib2.0-dev /usr/bin/glib-mkenums <RECENT-CTIME> -1180897461 1180897581 xpdf-utils /usr/bin/pdfimages <RECENT-CTIME> -1180897461 1180897472 dnsutils /usr/bin/nsupdate <RECENT-CTIME> -1180897461 1180897545 ppp /usr/sbin/pppdump <RECENT-CTIME> -1180897461 1180897509 libglib-perl /usr/lib/perl5/Glib.pm <RECENT-CTIME> -1180897461 1180897576 wodim /usr/bin/wodim <RECENT-CTIME> -1180897461 1180897556 python2.4-minimal /usr/bin/python2.4 <RECENT-CTIME> -1180897461 1180897483 desktop-file-utils /usr/bin/update-desktop-database <RECENT-CTIME> -1180897461 1180897571 syslinux /usr/bin/mkdiskimage <RECENT-CTIME> -1180897461 1180897550 python2.4-dev /usr/include/python2.4/weakrefobject.h <RECENT-CTIME> -1180897461 1180897538 noiz2sa /usr/bin/noiz2sa <RECENT-CTIME> -1180897461 1180897574 unpaper /usr/bin/unpaper <RECENT-CTIME> -1180897461 1180897463 libssl-dev /usr/include/openssl/pqueue.h <RECENT-CTIME> -1180897461 1180897560 ruby1.8 /usr/bin/testrb1.8 <RECENT-CTIME> -1180897461 1180897504 libatk1.0-dev /usr/include/atk-1.0/atk/atk-enum-types.h <RECENT-CTIME> -1180897461 1180897465 man-db /usr/sbin/accessdb <RECENT-CTIME> -1180897449 1180897459 xutils-dev /usr/bin/gccmakedep <RECENT-CTIME> -1180897449 1180897454 libxmu-dev /usr/lib/libXmu.a <RECENT-CTIME> -1180897449 1180897452 libpng12-dev /usr/bin/libpng-config <RECENT-CTIME> -1180897449 1180897451 libfreetype6-dev /usr/include/ft2build.h <RECENT-CTIME> -1180897449 1180897458 xbase-clients /usr/bin/xwud <RECENT-CTIME> -1180897441 1180897447 libxrandr-dev /usr/lib/libXrandr.a <RECENT-CTIME> -1180897441 1180897443 libxau-dev /usr/lib/libXau.a <RECENT-CTIME> -1180897441 1180897446 libxinerama-dev /usr/lib/libXinerama.a <RECENT-CTIME> -1180897441 1180897446 libxmu-headers /usr/include/X11/Xmu/Xct.h <RECENT-CTIME> -1180897441 1180897443 libsm-dev /usr/lib/libSM.a <RECENT-CTIME> -1180897441 1180897447 libxt-dev /usr/include/X11/Shell.h <RECENT-CTIME> -1180897441 1180897442 flex /usr/lib/libl.a <RECENT-CTIME> -1180897441 1180897445 libxcursor-dev /usr/lib/libXcursor.a <RECENT-CTIME> -1180897441 1180897445 libxext-dev /usr/lib/libXext.a <RECENT-CTIME> -1180897441 1180897444 libxrender-dev /usr/include/X11/extensions/Xrender.h <RECENT-CTIME> -1180897441 1180897444 libxfixes-dev /usr/lib/libXfixes.a <RECENT-CTIME> -1180897441 1180897448 zlib1g-dev /usr/include/zconf.h <RECENT-CTIME> -1180897435 1180897435 libncurses5-dev /usr/lib/libcurses.a <RECENT-CTIME> -1180897433 1180897434 sed /bin/sed <RECENT-CTIME> -1180897431 1180897432 ncurses-bin /usr/bin/reset <RECENT-CTIME> -1180897430 1180897431 mktemp /bin/mktemp <RECENT-CTIME> -1180897427 1180897429 findutils /usr/bin/updatedb <RECENT-CTIME> -1180897426 1180897427 diff /usr/bin/sdiff <RECENT-CTIME> -1180897423 1180897423 debianutils /usr/bin/which <RECENT-CTIME> -1180787194 1180787195 libept-dev /usr/lib/libept.a <RECENT-CTIME> -1180774498 1180774499 tagcoll /usr/bin/tagcoll <RECENT-CTIME> -1180774498 1180774498 libtagcoll2-dev /usr/include/tagcoll-2.0.6/tagcoll/utils/set.h <RECENT-CTIME> -1180627127 1180627144 libgtkmm-2.4-doc /usr/share/doc/libgtkmm-2.4-doc/examples/treemodelcustom/exampletreemodel.h <RECENT-CTIME> -1180627127 1180627142 libgtkmm-2.4-dev /usr/include/atkmm-1.6/atkmm.h <RECENT-CTIME> -1180614103 1180614123 reportbug-ng /usr/bin/rng <RECENT-CTIME> -1180608501 1180608507 rkhunter /usr/bin/rkhunter <RECENT-CTIME> -1180608501 1180608504 cdbs /usr/bin/cdbs-edit-patch <RECENT-CTIME> -1180608501 1180608505 python-nose /usr/bin/nosetests <RECENT-CTIME> -1180608501 1180608504 libimage-info-perl /usr/share/perl5/Image/Info/SVG.pm <RECENT-CTIME> -1180526809 1180526811 vim-scripts /usr/bin/vimplate <RECENT-CTIME> -1180460243 1180460244 hg-load-dirs /usr/bin/hg_load_dirs <RECENT-CTIME> -1180460243 1180460245 hg-buildpackage /usr/bin/hg-markdeb <RECENT-CTIME> -1180460221 1180460242 dpkg /sbin/start-stop-daemon <RECENT-CTIME> -1180460221 1180460240 dpkg-dev /usr/bin/dpkg-source <RECENT-CTIME> -1180460161 1180460188 binutils /usr/bin/ld <RECENT-CTIME> -1180460161 1180460196 libc6 /sbin/ldconfig <RECENT-CTIME> -1180460161 1180460190 libc6-dev /usr/include/syscall.h <RECENT-CTIME> -1180460161 1180460193 locales /usr/sbin/locale-gen <RECENT-CTIME> -1180459910 1180459953 linux-image-2.6.21-1-amd64 /boot/System.map-2.6.21-1-amd64 <RECENT-CTIME> -1180459910 1180459965 python-setuptools /usr/bin/easy_install-2.4 <RECENT-CTIME> -1180459910 1180459939 linux-headers-2.6.21-1 /usr/src/linux-headers-2.6.21-1/include/asm-generic/audit_change_attr.h <RECENT-CTIME> -1180254024 1180254033 eric /usr/bin/eric <RECENT-CTIME> -1180254024 1180254030 bzr-builddeb /usr/bin/bzr-buildpackage <RECENT-CTIME> -1180081502 1180081530 python-turbogears /usr/bin/tg-admin <RECENT-CTIME> -1180000910 1180000928 libwibble-dev /usr/lib/libwibble.a <RECENT-CTIME> -1179997203 1179997209 libdballef-dev /usr/lib/libdballef.a <RECENT-CTIME> -1179997203 1179997209 dballe /usr/bin/dbamsg <RECENT-CTIME> -1179997203 1179997210 python-dballe /usr/share/pycentral/python-dballe/site-packages/_dballe.a <RECENT-CTIME> -1179930200 1179930229 provami /usr/bin/provami <RECENT-CTIME> -1179908628 1179908651 doc-base /usr/share/perl5/Debian/DocBase/Common.pm <RECENT-CTIME> -1179908628 1179908651 apt-listchanges /usr/bin/apt-listchanges <RECENT-CTIME> -1179908628 1179909194 privoxy /usr/sbin/privoxy <RECENT-CTIME> -1179908628 1179908648 reportbug /usr/bin/querybts <RECENT-CTIME> -1179692174 1179692199 xclip /usr/bin/xclip <RECENT-CTIME> -1179657476 1179657496 openbox /usr/include/openbox/3.3/openbox/parse.h <RECENT-CTIME> -1179657476 1179657496 obconf /usr/bin/obconf <RECENT-CTIME> -1179655030 1179655048 python2.5-minimal /usr/bin/python2.5 <RECENT-CTIME> -1179655030 1179655034 bzr /usr/bin/bzr <RECENT-CTIME> -1179655030 1179655052 uswsusp /usr/sbin/s2ram <RECENT-CTIME> -1179655030 1179655048 pm-utils /usr/sbin/pm-hibernate <RECENT-CTIME> -1179655030 1179655050 python2.5 /usr/bin/pdb2.5 <RECENT-CTIME> -1179655030 1179655037 gs-common /usr/bin/ps2txt <RECENT-CTIME> -1179655030 1179655035 dpatch /usr/bin/dpatch-get-origtargz <RECENT-CTIME> -1179655030 1179655031 python-support /usr/sbin/update-python-modules <RECENT-CTIME> -1179655026 1179655028 libhsqldb-java /usr/bin/hsqldb-databasemanagerswing <RECENT-CTIME> -1179428033 1179428056 rake /usr/bin/rake <RECENT-CTIME> -1179428033 1179428055 c++-annotations /usr/share/doc/c++-annotations/examples/yo/overloading/examples/binary3.h <RECENT-CTIME> -1179428033 1179428060 ucf /usr/bin/ucfq <RECENT-CTIME> -1179256205 1179256236 kernel-package /usr/sbin/kernel-packageconfig <RECENT-CTIME> -1179256170 1179256192 linux-image-2.6.18-4-amd64 /boot/System.map-2.6.18-4-amd64 <RECENT-CTIME> -1178982037 1178982070 pydb /usr/bin/pydb <RECENT-CTIME> -1178982037 1178982071 python-pastescript /usr/bin/paster <RECENT-CTIME> -1178982037 1178982057 x11proto-input-dev /usr/include/X11/extensions/XIproto.h <RECENT-CTIME> -1178982037 1178982058 clive /usr/bin/clive <RECENT-CTIME> -1178783830 1178783848 meld /usr/bin/meld <RECENT-CTIME> -1178783830 1178783847 libtimedate-perl /usr/share/perl5/Date/Language.pm <RECENT-CTIME> -1178783830 1178783838 dvipdfmx /usr/bin/dvipdfmx <RECENT-CTIME> -1178783830 1178783882 texlive-latex-extra /usr/share/doc/texlive-doc/latex/register/Reg_macro.pm.gz <RECENT-CTIME> -1178783830 1178783847 lintian /usr/share/lintian/lib/Dep.pm <RECENT-CTIME> -1178783830 1178783846 ipython /usr/bin/pycolor <RECENT-CTIME> -1178783824 1178783826 texlive-base-bin /usr/bin/oxdvi <RECENT-CTIME> -1178783824 1178783826 texlive-latex-base /usr/bin/latex <RECENT-CTIME> -1178783824 1178783827 texlive-pdfetex /usr/bin/pdfopen <RECENT-CTIME> -1178633976 1178634000 xvfb /usr/bin/xvfb-run <RECENT-CTIME> -1178633976 1178634001 python-dogtail /usr/bin/dogtail-recorder <RECENT-CTIME> -1178142411 1178142444 liblocale-maketext-simple-perl /usr/share/perl5/Locale/Maketext/Simple.pm <OLD> -1178142411 1178142442 debhelper /usr/share/perl5/Debian/Debhelper/Dh_Version.pm <OLD> -1178128394 1178128416 python-imaging /usr/include/python2.3/Imaging.h <OLD> -1178128394 1178128416 keyjnote /usr/bin/keyjnote <OLD> -1178015044 1178015050 python-docutils /usr/bin/rst2newlatex <OLD> -1178015044 1178015066 qt4-dev-tools /usr/bin/linguist-qt4 <OLD> -1178015044 1178015066 wormux /usr/games/wormux <OLD> -1178015044 1178015055 python-numpy /usr/bin/f2py <OLD> -1178015044 1178015054 python-numpy-dev /usr/lib/python2.4/site-packages/numpy/f2py/src/fortranobject.h <OLD> -1178014144 1178014225 xml-core /usr/sbin/update-xmlcatalog <OLD> -1178014144 1178014226 zenity /usr/bin/gdialog <OLD> -1178014144 1178014146 r-base-core /usr/lib/R/bin/R <OLD> -1178014144 1178014228 svn-buildpackage /usr/share/svn-buildpackage/SDCommon.pm <OLD> -1178014144 1178014184 rdoc1.8 /usr/bin/rdoc1.8 <OLD> -1178014144 1178014185 recode /usr/bin/recode <OLD> -1178014144 1178014203 tex-common /usr/bin/update-updmap <OLD> -1178014144 1178014190 scribus-ng /usr/bin/scribus-ng <OLD> -1178014144 1178014213 unrar /usr/bin/unrar <OLD> -1178014144 1178014221 xfsprogs /sbin/xfs_repair <OLD> -1178014144 1178014222 xine-ui /usr/bin/xine-bugreport <OLD> -1178014144 1178014201 sudo /usr/sbin/visudo <OLD> -1178014144 1178014225 xplanet /usr/bin/xplanet <OLD> -1178014144 1178014212 unp /usr/bin/unp <OLD> -1178014144 1178014186 rpm /usr/bin/rpmquery <OLD> -1178014144 1178014202 tcptraceroute /usr/bin/tcptraceroute <OLD> -1178014144 1178014220 wpasupplicant /sbin/wpa_cli <OLD> -1178014144 1178014167 ri /usr/bin/ri <OLD> -1178014144 1178014199 smartmontools /usr/sbin/smartctl <OLD> -1178014144 1178014206 tsocks /usr/bin/validateconf <OLD> -1178014144 1178014187 rsync /usr/bin/rsync <OLD> -1178014144 1178014167 ri1.8 /usr/bin/ri1.8 <OLD> -1178014144 1178014216 valgrind /usr/include/valgrind/pub_tool_libcassert.h <OLD> -1178014140 1178014141 x11proto-randr-dev /usr/include/X11/extensions/randrproto.h <OLD> -1178014140 1178014140 x11proto-core-dev /usr/include/X11/Xwinsock.h <OLD> -1178014119 1178014125 sun-java5-jdk /usr/lib/jvm/java-1.5.0-sun-1.5.0.11/include/jdwpTransport.h <OLD> -1178014119 1178014137 x11-common /usr/bin/X <OLD> -1178014119 1178014121 sun-java5-demo /usr/lib/jvm/java-1.5.0-sun-1.5.0.11/demo/jvmti/heapTracker/src/heapTracker.h <OLD> -1178014119 1178014130 sun-java5-bin /usr/lib/jvm/java-1.5.0-sun-1.5.0.11/bin/java <OLD> -1178014098 1178014117 tar /bin/tar <OLD> -1178012580 1178012681 libqt4-core /usr/bin/qdbus <OLD> -1178012580 1178012663 liblocale-maketext-lexicon-perl /usr/share/perl5/Locale/Maketext/Lexicon/Auto.pm <OLD> -1178012580 1178012672 pcscd /usr/sbin/update-reader.conf <OLD> -1178012580 1178012694 libwmf-bin /usr/bin/wmf2eps <OLD> -1178012580 1178012731 proj /usr/bin/invgeod <OLD> -1178012580 1178012713 module-assistant /usr/bin/m-a <OLD> -1178012580 1178012597 libavahi-common-dev /usr/include/avahi-common/rlist.h <OLD> -1178012580 1178012661 libhtml-parser-perl /usr/lib/perl5/HTML/Filter.pm <OLD> -1178012580 1178012592 libbonobo2-dev /usr/include/bonobo-activation-2.0/bonobo-activation/bonobo-activation-version.h <OLD> -1178012580 1178012612 file-roller /usr/bin/file-roller <OLD> -1178012580 1178012640 libflash-swfplayer /usr/bin/swfplayer <OLD> -1178012580 1178012580 libgpg-error-dev /usr/bin/gpg-error-config <OLD> -1178012580 1178012590 nfs-common /sbin/rpc.lockd <OLD> -1178012580 1178012582 libgcrypt11-dev /usr/bin/libgcrypt-config <OLD> -1178012580 1178012740 pstoedit /usr/bin/pstoedit <OLD> -1178012580 1178012621 intltool /usr/bin/intltool-prepare <OLD> -1178012580 1178012633 libcaca0 /usr/lib/caca/libgl_plugin.a <OLD> -1178012580 1178012656 libhdf4g-dev /usr/include/hdf/hfile.h <OLD> -1178012580 1178012591 libidl-dev /usr/bin/libIDL-config-2 <OLD> -1178012580 1178012598 libavahi-glib-dev /usr/include/avahi-glib/glib-malloc.h <OLD> -1178012580 1178012583 libopencdk8-dev /usr/bin/opencdk-config <OLD> -1178012580 1178012585 libkrb5-dev /usr/include/profile.h <OLD> -1178012580 1178012690 libsvn-mirror-perl /usr/bin/svn-mirror <OLD> -1178012580 1178012635 libconvert-binhex-perl /usr/share/perl5/Convert/BinHex.pm <OLD> -1178012580 1178012636 xdg-utils /usr/bin/xdg-desktop-menu <OLD> -1178012580 1178012595 libbonoboui2-dev /usr/lib/libglade/2.0/libbonobo.a <OLD> -1178012580 1178012626 irb /usr/bin/irb <OLD> -1178012580 1178012729 orbit2 /usr/bin/typelib-dump <OLD> -1178012580 1178012594 libbonobo2-common /usr/sbin/bonobo-activation-sysconf <OLD> -1178012580 1178012714 mplayer /usr/bin/gmplayer <OLD> -1178012580 1178012620 imageindex /usr/bin/imageindex <OLD> -1178012580 1178012599 libgnomeui-dev /usr/lib/libgnomeui-2.a <OLD> -1178012580 1178012635 libclone-perl /usr/lib/perl5/Clone.pm <OLD> -1178012580 1178012678 libqt4-dev /usr/share/qt4/bin/lupdate <OLD> -1178012580 1178012592 liborbit2-dev /usr/include/orbit-2.0/ORBitservices/CosNaming.h <OLD> -1178012580 1178012649 libgksu2-0 /usr/bin/gksu-properties <OLD> -1178012580 1178012732 popularity-contest /usr/sbin/popcon-largest-unused <OLD> -1178012580 1178012729 p7zip-full /usr/bin/7za <OLD> -1178012580 1178012695 libxml-simple-perl /usr/share/perl5/XML/Simple.pm <OLD> -1178012580 1178012635 libdata-hierarchy-perl /usr/share/perl5/Data/Hierarchy.pm <OLD> -1178012580 1178012636 libdigest-sha1-perl /usr/lib/perl5/Digest/SHA1.pm <OLD> -1178012580 1178012733 postgresql-autodoc /usr/bin/postgresql_autodoc <OLD> -1178012580 1178012621 initramfs-tools /usr/sbin/mkinitramfs <OLD> -1178012580 1178012597 libavahi-client-dev /usr/include/avahi-client/publish.h <OLD> -1178012580 1178012640 libfile-chdir-perl /usr/share/perl5/File/chdir.pm <OLD> -1178012580 1178012712 mercurial /usr/bin/hg <OLD> -1178012580 1178012626 irb1.8 /usr/bin/irb1.8 <OLD> -1178012580 1178012665 libmysqlclient15-dev /usr/bin/mysql_config <OLD> -1178012580 1178012662 libhtml-tree-perl /usr/share/perl5/HTML/Element/traverse.pm <OLD> -1178012580 1178012584 libtasn1-3-dev /usr/bin/libtasn1-config <OLD> -1178012580 1178012634 libcfitsio-dev /usr/lib/libcfitsio.a <OLD> -1178012580 1178012661 libhtml-template-perl /usr/share/perl5/HTML/Template.pm <OLD> -1178012580 1178012691 libwmf-dev /usr/include/libwmf/defs.h <OLD> -1178012580 1178012689 libsdl-perl /usr/lib/perl5/SDL_perl.pm <OLD> -1178012580 1178012713 moreutils /usr/bin/zrun <OLD> -1178012580 1178012664 libmime-perl /usr/share/perl5/MIME/Parser/Reader.pm <OLD> -1178012580 1178012628 kdiff3 /usr/bin/kdiff3 <OLD> -1178012580 1178012587 mutt /usr/bin/mutt_dotlock <OLD> -1178012580 1178012650 libglibmm-2.4-dev /usr/include/glibmm-2.4/glibmm_generate_extra_defs/generate_extra_defs.h <OLD> -1178012525 1178012530 mysql-server-5.0 /usr/sbin/ndb_mgmd <OLD> -1178012511 1178012522 libdbd-mysql-perl /usr/lib/perl5/Bundle/DBD/mysql.pm <OLD> -1178012511 1178012524 mysql-client-5.0 /usr/sbin/mysqlmanager <OLD> -1178012511 1178012513 libxdmcp-dev /usr/include/X11/Xdmcp.h <OLD> -1178012511 1178012512 imlib-base /usr/bin/imlib-config <OLD> -1178012511 1178012512 libice-dev /usr/include/X11/ICE/ICEconn.h <OLD> -1178012511 1178012514 libxft-dev /usr/bin/xft-config <OLD> -1178011412 1178011420 m4 /usr/bin/m4 <OLD> -1178011412 1178011535 gnome-volume-manager /usr/bin/gnome-volume-manager-gthumb <OLD> -1178011412 1178011442 dvipng /usr/bin/dvigif <OLD> -1178011412 1178011516 gnome-common /usr/bin/gnome-doc-common <OLD> -1178011412 1178011470 gdm /usr/bin/gdmXnest <OLD> -1178011412 1178011540 gpsdrive /usr/lib/libfly.a <OLD> -1178011412 1178011440 dvbstream /usr/bin/dvbstream <OLD> -1178011412 1178011495 ggobi /usr/include/ggobi/GGobiEvents.h <OLD> -1178011412 1178011541 make /usr/bin/make <OLD> -1178011412 1178011468 gksu /usr/bin/gksudo <OLD> -1178011412 1178011442 exiftags /usr/bin/exiftime <OLD> -1178011412 1178011440 dput /usr/bin/dcut <OLD> -1178011412 1178011539 gpsd-clients /usr/bin/xgpsspeed <OLD> -1178011412 1178011536 gnupg-agent /usr/bin/gpgkey2ssh <OLD> -1178011412 1178011443 gaim /usr/bin/gaim-remote <OLD> -1178011412 1178011515 gnome-bin /usr/bin/new-object <OLD> -1178011412 1178011426 darcs-load-dirs /usr/bin/darcs_load_dirs <OLD> -1178011412 1178011415 klogd /sbin/klogd <OLD> -1178011412 1178011415 laptop-detect /usr/sbin/laptop-detect <OLD> -1178011412 1178011549 mc /usr/bin/mcedit <OLD> -1178011412 1178011542 lftp /usr/bin/lftp <OLD> -1178011412 1178011674 gnome-applets /usr/bin/cpufreq-selector <OLD> -1178011412 1178011546 mapserver-bin /usr/bin/scalebar <OLD> -1178011412 1178011552 debootstrap /usr/sbin/debootstrap <OLD> -1178011412 1178011494 germinate /usr/bin/germinate <OLD> -1178011412 1178011426 cowdancer /usr/sbin/cowbuilder <OLD> -1178011412 1178011547 markdown /usr/share/perl5/Text/Markdown.pm <OLD> -1178011412 1178011415 dhcp3-common /usr/bin/omshell <OLD> -1178011412 1178011536 gpgsm /usr/bin/gpgsm-gencert.sh <OLD> -1178011412 1178011460 gnome-session /usr/bin/gnome-wm <OLD> -1178011412 1178011538 gpsd /usr/include/libgpsmm.h <OLD> -1178011412 1178011425 cogito /usr/bin/cg-version <OLD> -1178011412 1178011424 cdrdao /usr/bin/cdrdao <OLD> -1178011412 1178011493 gedit /usr/bin/gedit <OLD> -1178011412 1178011414 debconf /usr/bin/debconf-show <OLD> -1178011412 1178011441 dvd+rw-tools /usr/bin/btcflash <OLD> -1178011412 1178011461 gnome-terminal /usr/bin/gnome-terminal.wrapper <OLD> -1178011412 1178011553 gnupg2 /usr/sbin/addgnupghome <OLD> -1178011412 1178011516 gnome-doc-utils /usr/bin/xml2po <OLD> -1178011412 1178011428 devscripts /usr/bin/cvs-debc <OLD> -1178011412 1178011427 debsums /usr/sbin/debsums_gen <OLD> -1178011412 1178011415 dhcp3-client /sbin/dhclient <OLD> -1178011412 1178011420 finger /usr/bin/finger <OLD> -1178011412 1178011426 svk-load-dirs /usr/bin/svk_load_dirs <OLD> -1178011412 1178011433 docbook-to-man /usr/bin/docbook-to-man <OLD> -1178011412 1178011441 dvbtune /usr/bin/dvbtune <OLD> -1178011412 1178011428 discover1 /sbin/discover <OLD> -1178011412 1178011552 aumix /usr/bin/aumix <OLD> -1178011401 1178011402 audacity /usr/bin/audacity <OLD> -1178011380 1178011381 dsniff /usr/sbin/dsniff <OLD> -1178010287 1178010316 swig /usr/bin/swig-1.3 <OLD> -1178010287 1178010294 vim-full /usr/bin/vim.full <OLD> -1178010287 1178010318 swig-examples /usr/share/doc/swig-examples/xml/example_xml.h <OLD> -1178010287 1178010323 wireshark /usr/bin/wireshark <OLD> -1178010287 1178010324 wireshark-common /usr/bin/editcap <OLD> -1178010287 1178010308 acpitool /usr/bin/acpitool <OLD> -1178010287 1178010304 telnet /usr/bin/telnet.netkit <OLD> -1178010287 1178010309 graphviz /usr/bin/neato <OLD> -1178010287 1178010300 vim-common /usr/bin/helpztags <OLD> -1178010287 1178010304 acpi /usr/bin/acpi <OLD> -1178010287 1178010313 postfix /usr/bin/mailq <OLD> -1178010287 1178010321 unison /usr/bin/unison-latest-stable <OLD> -1178010287 1178010326 xsltproc /usr/bin/xsltproc <OLD> -1178010287 1178010289 xtrans-dev /usr/include/X11/Xtrans/Xtransint.h <OLD> -1178010287 1178010327 nmap /usr/bin/nmap <OLD> -1178010287 1178010322 unison2.9.1 /usr/bin/unison-2.9.1 <OLD> -1178010287 1178010321 timidity /usr/bin/timidity <OLD> -1178010287 1178010293 vim-tiny /usr/bin/vim.tiny <OLD> -1178010287 1178010326 xterm /usr/bin/resize <OLD> -1178010287 1178010297 vim /usr/bin/vim.basic <OLD> -1178010287 1178010315 sqlite3 /usr/bin/sqlite3 <OLD> -1178010287 1178010299 vim-runtime /usr/bin/vimtutor <OLD> -1178010287 1178010310 offlineimap /usr/bin/offlineimap <OLD> -1178010287 1178010307 acpid /usr/sbin/acpid <OLD> -1178010287 1178010291 netcat /bin/netcat <OLD> -1178010287 1178010323 wammu /usr/bin/wammu <OLD> -1178010287 1178010292 sysklogd /sbin/syslogd <OLD> -1178010285 1178010286 gzip /bin/uncompress <OLD> -1177924535 1177924564 openoffice.org-calc /usr/bin/oocalc <OLD> -1177924535 1177924564 openoffice.org-math /usr/bin/oomath <OLD> -1177924535 1177924564 openoffice.org-writer /usr/bin/oowriter <OLD> -1177924535 1177924564 openoffice.org-base /usr/bin/oobase <OLD> -1177924535 1177924564 openoffice.org-impress /usr/bin/ooimpress <OLD> -1177924535 1177924564 openoffice.org-common /usr/bin/soffice <OLD> -1177924535 1177924564 openoffice.org-draw /usr/bin/oodraw <OLD> -1177884980 1177885000 scalpel /usr/bin/scalpel <OLD> -1177884980 1177885000 magicrescue /usr/bin/magicsort <OLD> -1177884980 1177884998 dosfstools /sbin/mkfs.vfat <OLD> -1177689577 1177689592 kismet /usr/bin/kismet_drone <OLD> -1177689577 1177689594 tomatoes /usr/games/tomatoes <OLD> -1177689577 1177689593 patchutils /usr/bin/lsdiff <OLD> -1177689577 1177689596 youtube-dl /usr/bin/youtube-dl <OLD> -1177537338 1166190405 mawk /usr/bin/mawk <OLD> -1177537338 1172691910 coreutils /usr/bin/sort <OLD> -1177537187 1166527002 dlocate /usr/bin/dlocate <OLD> -1177537187 1166980508 perl-base /usr/share/perl/5.8.8/bytes.pm <OLD> -1177537187 1166980507 perl /usr/lib/perl/5.8.8/Encode/Config.pm <OLD> -1177537187 1166190366 liblocale-gettext-perl /usr/lib/perl5/Locale/gettext.pm <OLD> -1177537186 1166980508 perl-suid /usr/bin/suidperl <OLD> -1177537170 1166190402 less /usr/bin/lessfile <OLD> -1177537088 1173474006 openssh-client /usr/bin/slogin <OLD> -1177536554 1173000156 epiphany-browser /usr/bin/epiphany-browser <OLD> -1177536440 1177159756 metacafe-dl /usr/bin/metacafe-dl <OLD> -1177536393 1171977084 liferea /usr/bin/liferea-bin <OLD> -1177526970 1173697161 acpi-support /usr/bin/acpi_fakekey <OLD> -1177525862 1175633735 xscreensaver /usr/bin/xscreensaver-command <OLD> -1177525861 1173000087 bsdutils /usr/bin/logger <OLD> -1177525860 1170084281 alsa-utils /usr/sbin/alsactl <OLD> -1177525860 1166190402 console-tools /usr/bin/chvt <OLD> -1177525860 1170794023 sysv-rc /usr/sbin/invoke-rc.d <OLD> -1177525860 1174317435 guessnet /usr/sbin/guessnet-ifupdown <OLD> -1177503973 1166980505 perl-modules /usr/share/perl/5.8.8/File/Copy.pm <OLD> -1177503973 1166372777 svk /usr/share/perl5/SVK/Editor/Delay.pm <OLD> -1177503971 1166372774 libsvn-perl /usr/lib/perl5/SVN/Core.pm <OLD> -1177503971 1166372773 libclass-autouse-perl /usr/share/perl5/Class/Autouse.pm <OLD> -1177503971 1166372775 liburi-perl /usr/share/perl5/URI/Escape.pm <OLD> -1177503971 1166372773 libperlio-via-dynamic-perl /usr/share/perl5/PerlIO/via/dynamic.pm <OLD> -1177503971 1166372774 libperlio-via-symlink-perl /usr/share/perl5/PerlIO/via/symlink.pm <OLD> -1177503971 1166372774 libperlio-eol-perl /usr/lib/perl5/PerlIO/eol.pm <OLD> -1177503971 1166372774 libsvn-simple-perl /usr/share/perl5/SVN/Simple/Edit.pm <OLD> -1177503971 1166372775 libyaml-perl /usr/share/perl5/YAML/Loader/Base.pm <OLD> -1177503969 1166372774 libregexp-shellish-perl /usr/share/perl5/Regexp/Shellish.pm <OLD> -1177503824 1166372775 libterm-readkey-perl /usr/lib/perl5/Term/ReadKey.pm <OLD> -1177503775 1166372773 libfile-type-perl /usr/share/perl5/File/Type.pm <OLD> -1177502279 1174946957 gnupg /usr/bin/gpg <OLD> -1177502119 1174946957 gpgv /usr/bin/gpgv <OLD> -1177502117 1174946955 aptitude /usr/bin/aptitude <OLD> -1177502114 1172691934 apt /usr/bin/apt-key <OLD> -1177502023 1166190401 groff-base /usr/bin/nroff <OLD> -1177501942 1166190405 wget /usr/bin/wget <OLD> -1177498403 1166190613 laptop-mode-tools /usr/sbin/laptop_mode <OLD> -1177497857 1170084280 w3m /usr/bin/w3m <OLD> -1177493293 1166190401 mime-support /usr/bin/print <OLD> -1177489700 1175126570 udev /usr/bin/udevinfo <OLD> -1177454644 1177159754 tasks /usr/bin/tasks <OLD> -1177454361 1171415966 imagemagick /usr/bin/display <OLD> -1177436167 1166190401 procps /usr/bin/top <OLD> -1177409235 1167739438 lbdb /usr/bin/lbdbq <OLD> -1177408907 1169982592 evince /usr/bin/evince <OLD> -1177408326 1166190407 traceroute /usr/bin/traceroute.lbl <OLD> -1177407856 1166190408 unzip /usr/bin/zipinfo <OLD> -1177354768 1175443118 azureus /usr/bin/azureus <OLD> -1177349852 1169982582 dict /usr/bin/dict <OLD> -1177346933 1166372777 subversion /usr/bin/svn <OLD> -1177345009 1166190409 grep /usr/bin/rgrep <OLD> -1177343013 1166190413 libtext-wrapi18n-perl /usr/share/perl5/Text/WrapI18N.pm <OLD> -1177343013 1166190366 libtext-charwidth-perl /usr/lib/perl5/Text/CharWidth.pm <OLD> -1177343013 1166190366 libtext-iconv-perl /usr/lib/perl5/Text/Iconv.pm <OLD> -1177343010 1172691933 apt-utils /usr/bin/apt-extracttemplates <OLD> -1177338778 1167557339 menu /usr/sbin/install-menu <OLD> -1177338739 1177338759 k3b /usr/bin/k3b <OLD> -1177338733 1166190399 xfonts-utils /usr/bin/mkfontscale <OLD> -1177338731 1171541599 defoma /usr/bin/defoma-font <OLD> -1177338731 1173127622 fontconfig /usr/bin/fc-cache <OLD> -1177338697 1174593895 update-inetd /usr/sbin/update-inetd <OLD> -1177338601 1173553214 passwd /usr/sbin/groupdel <OLD> -1177338601 1170084279 adduser /usr/sbin/delgroup <OLD> -1177338597 1169982630 cron /usr/bin/crontab <OLD> -1177337194 1166544993 qalculate-gtk /usr/bin/qalculate-gtk <OLD> -1177324149 1175768481 ekiga /usr/bin/ekiga <OLD> -1177323796 1166190406 lsof /usr/bin/lsof <OLD> -1177323316 1166190401 pciutils /bin/lspci <OLD> -1177317323 1166190407 gs-esp /usr/bin/gs-esp <OLD> -1177278694 1166493256 buffy /usr/bin/buffy <OLD> -1177264323 1177171053 dosbox /usr/bin/dosbox <OLD> -1177262780 1174946965 iceweasel /usr/bin/iceweasel <OLD> -1177261570 1166492130 pv /usr/bin/pv <OLD> -1177258837 1166527580 gqview /usr/bin/gqview <OLD> -1177258552 1175633862 xscreensaver-gl /usr/bin/xscreensaver-gl-helper <OLD> -1177258530 1166526991 compiz-core /usr/bin/compiz.real <OLD> -1177258530 1166526991 compiz-gtk /usr/bin/gtk-window-decorator <OLD> -1177258529 1166190406 xserver-xorg-input-synaptics /usr/bin/synclient <OLD> -1177258528 1166980531 gsynaptics /usr/bin/gsynaptics-init <OLD> -1177258527 1169039173 cpp-4.1 /usr/bin/x86_64-linux-gnu-cpp-4.1 <OLD> -1177258527 1166526991 mesa-utils /usr/bin/glxinfo <OLD> -1177258527 1169039173 cpp /usr/bin/cpp <OLD> -1177258527 1166493252 bluez-gnome /usr/bin/bluetooth-applet <OLD> -1177258526 1173000162 gnome-panel /usr/bin/gnome-panel <OLD> -1177258526 1173805976 nautilus /usr/bin/nautilus <OLD> -1177258525 1170084317 vino /usr/bin/vino-session <OLD> -1177258523 1173000161 gnome-control-center /usr/bin/gnome-settings-daemon <OLD> -1177258523 1166980517 gnome-keyring /usr/bin/gnome-keyring-daemon <OLD> -1177258518 1166190401 dbus /usr/bin/dbus-launch <OLD> -1177258518 1168076532 scim /usr/bin/scim <OLD> -1177258517 1166190402 xutils /usr/bin/sessreg <OLD> -1177258490 1175426296 apache2.2-common /usr/sbin/apache2ctl <OLD> -1177258488 1166190613 anacron /usr/sbin/anacron <OLD> -1177258488 1166190615 at /usr/sbin/atd <OLD> -1177258488 1175866318 apache2-mpm-prefork /usr/sbin/apache2 <OLD> -1177258487 1166493249 bluez-utils /usr/sbin/sdpd <OLD> -1177258485 1175768456 xserver-xorg-core /usr/bin/Xorg <OLD> -1177258482 1174946960 openbsd-inetd /usr/sbin/inetd <OLD> -1177258482 1166190615 lpr /usr/sbin/lpd <OLD> -1177258480 1169982583 dictd /usr/sbin/dictd <OLD> -1177258471 1170794026 cupsys /usr/sbin/cupsd <OLD> -1177258471 1166190411 cpufrequtils /usr/bin/cpufreq-set <OLD> -1177258470 1174389114 postgresql-client-7.4 /usr/lib/postgresql/7.4/bin/psql <OLD> -1177258464 1174389115 postgresql-7.4 /usr/lib/postgresql/7.4/bin/postmaster <OLD> -1177258460 1166190613 vbetool /usr/sbin/vbetool <OLD> -1177258458 1170877544 schroot /usr/bin/schroot <OLD> -1177258458 1173000091 util-linux /usr/bin/setterm <OLD> -1177256339 1177256363 gcom /usr/bin/sigmon <OLD> -1177220593 1166190404 procmail /usr/bin/procmail <OLD> -1177220578 1173473991 e2fsprogs /usr/bin/lsattr <OLD> -1177220515 1173000123 tcpd /usr/sbin/tcpd <OLD> -1177220474 1166190615 logrotate /usr/sbin/logrotate <OLD> -1177220149 1176889700 faubackup /usr/sbin/faubackup <OLD> -1177220121 1166190366 libapt-pkg-perl /usr/lib/perl5/AptPkg.pm <OLD> -1177220121 1166493229 apt-show-versions /usr/bin/apt-show-versions <OLD> -1177174821 1175720153 kdelibs4c2a /usr/bin/dcopserver_shutdown <OLD> -1177172601 1175633687 konqueror /usr/bin/konqueror <OLD> -1177171031 1177171053 libsdl-sound1.2 /usr/bin/playsound <OLD> -1177168342 1177159756 pangzero /usr/games/pangzero <OLD> -1177168314 1177159750 gt5 /usr/bin/gt5 <OLD> -1177168292 1172787014 lynx /usr/bin/lynx.stable <OLD> -1177168077 1177159749 gaia /usr/bin/gaia <OLD> -1177166731 1177159749 freedroidrpg /usr/games/freedroidRPG <OLD> -1177166619 1177159719 balder2d /usr/games/balder2d <OLD> -1177159695 1177159754 screenkast /usr/lib/libisdvncauth.a <OLD> -1177159695 1177159755 wfo /usr/bin/wfo <OLD> -1177159695 1177159751 isdexport /usr/bin/isdexport <OLD> -1177159695 1177159719 cryopid /usr/bin/freeze <OLD> -1177149141 1166190411 xdiskusage /usr/bin/xdiskusage <OLD> -1177068294 1166541760 driftnet /usr/bin/driftnet <OLD> -1177067033 1173697184 tcpdump /usr/sbin/tcpdump <OLD> -1177063016 1173553213 whiptail /usr/bin/whiptail <OLD> -1177060535 1170084291 gnome-system-tools /usr/bin/network-admin <OLD> -1177060268 1168942055 gnome-cups-manager /usr/bin/gnome-cups-manager <OLD> -1177057911 1169039178 g++-4.1 /usr/bin/x86_64-linux-gnu-g++-4.1 <OLD> -1177057911 1169039178 gcc-4.1 /usr/bin/x86_64-linux-gnu-gcc-4.1 <OLD> -1177057911 1169039178 gcc /usr/bin/gcc <OLD> -1177057911 1169039178 g++ /usr/bin/g++ <OLD> -1177057909 1169039179 libstdc++6-4.1-dev /usr/include/c++/4.1.2/x86_64-linux-gnu/bits/gthr-default.h <OLD> -1177057909 1167557354 libtut-dev /usr/include/tut_reporter.h <OLD> -1177057908 1171541560 linux-kernel-headers /usr/include/linux/limits.h <OLD> -1177057891 1166527570 doxygen /usr/bin/doxygen <OLD> -1177057884 1177057756 libxml++2.6-dev /usr/include/libxml++-2.6/libxml++/noncopyable.h <OLD> -1177057830 1166526982 pkg-config /usr/bin/pkg-config <OLD> -1177057829 1169039177 gfortran-4.1 /usr/bin/x86_64-linux-gnu-gfortran-4.1 <OLD> -1177057829 1169039177 gfortran /usr/bin/gfortran <OLD> -1177057829 1169039177 libgfortran1-dev /usr/lib/libgfortranbegin.a <OLD> -1177056234 1177056236 adept-installer /usr/bin/adept_installer <OLD> -1177056234 1177056235 adept-manager /usr/bin/adept_manager <OLD> -1177056234 1177056236 adept-notifier /usr/bin/adept_notifier <OLD> -1177056234 1177056236 adept-updater /usr/bin/adept_updater <OLD> -1177055863 1177055753 synaptic /usr/sbin/synaptic <OLD> -1177055757 1171977196 scrollkeeper /usr/bin/scrollkeeper-config <OLD> -1177053904 1166190407 gimp /usr/bin/gimp <OLD> -1177053791 1166542134 an /usr/games/an <OLD> -1177053745 1166545003 sl /usr/bin/sl <OLD> -1177053465 1177053414 xjokes /usr/games/yasiti <OLD> -1177048429 1166542775 pwgen /usr/bin/pwgen <OLD> -1177009159 1166491974 bash /usr/bin/clear_console <OLD> -1176987113 1168510531 ocaml-base-nox /usr/bin/ocamlrun <OLD> -1176987113 1166493270 polygen /usr/bin/polygen <OLD> -1176987113 1166493278 polygen-data /usr/bin/polyfind <OLD> -1176986681 1172076603 byzanz /usr/bin/byzanz-record <OLD> -1176986340 1173870371 gnome-utils /usr/bin/gnome-panel-screenshot <OLD> -1176985997 1166541760 vcdimager /usr/bin/vcdxrip <OLD> -1176985121 1172822391 metacity /usr/bin/metacity <OLD> -1176978320 1166544993 qalc /usr/bin/qalc <OLD> -1176969527 1166190405 pmount /usr/bin/pumount <OLD> -1176969171 1170794019 sysvinit-utils /usr/bin/mesg <OLD> -1176928025 1166372773 libio-digest-perl /usr/share/perl5/IO/Digest.pm <OLD> -1176893896 1166190401 vlock /usr/bin/vlock <OLD> -1176893459 1166190404 zip /usr/bin/zip <OLD> -1176886834 1166190403 gdb /usr/bin/gdb <OLD> -1176886361 1166190409 gnome-system-monitor /usr/bin/gnome-system-monitor <OLD> -1176879727 1174236295 cvs /usr/bin/cvs <OLD> -1176838154 1166526994 dctrl-tools /usr/bin/grep-aptavail <OLD> -1176833972 1173474025 yelp /usr/bin/gnome-help <OLD> -1176814275 1166493281 bogosort /usr/bin/bogosort <OLD> -1176813444 1176208146 filters /usr/games/chef <OLD> -1176813444 1166526993 cowsay /usr/bin/cowthink <OLD> -1176810639 1169026305 qgis /usr/bin/qgis <OLD> -1176804771 1176804745 vigor /usr/bin/vigor <OLD> -1176793976 1168364125 bsdmainutils /usr/bin/col <OLD> -1176726238 1173553208 login /usr/bin/sg <OLD> -1176669692 1166190411 gucharmap /usr/bin/charmap <OLD> -1176564336 1174236322 libgnomevfs2-bin /usr/bin/gnomevfs-copy <OLD> -1176563873 1174389116 postgresql-contrib-7.4 /usr/lib/postgresql/7.4/bin/pg_autovacuum <OLD> -1176468989 1175699053 msat /usr/bin/msat <OLD> -1176468157 1166526994 debtags-edit /usr/bin/debtags-edit <OLD> -1176462129 1176462102 qtparted /usr/sbin/qtparted <OLD> -1176455120 1174312930 arping /usr/sbin/arping <OLD> -1176452788 1167120719 dvb-utils /usr/bin/scan <OLD> -1176452788 1170892774 ruby-prof /usr/bin/ruby-prof <OLD> -1176452788 1166545116 libglade2-ruby /usr/bin/ruby-glade-create-template <OLD> -1176452781 1166527235 libjpeg-progs /usr/bin/rdjpgcom <OLD> -1176452781 1166372529 gettext /usr/bin/recode-sr-latin <OLD> -1176452781 1168365726 libgcj-common /usr/bin/rebuild-gcj-db <OLD> -1176452781 1173647376 rar /usr/bin/rar <OLD> -1176452781 1166190407 sharutils /usr/bin/remsync <OLD> -1176452781 1167285834 rcs /usr/bin/rcsmerge <OLD> -1176452781 1173000171 installation-report /usr/bin/report-hw <OLD> -1176452780 1166542752 xapian-tools /usr/bin/quest <OLD> -1176452779 1170011132 pychecker /usr/bin/pychecker <OLD> -1176452779 1166544994 qemuctl /usr/bin/qemuctl <OLD> -1176452779 1168429414 qemu-launcher /usr/bin/qemu-launcher <OLD> -1176452778 1167406185 debian-goodies /usr/bin/popbugs <OLD> -1176452778 1166527249 psutils /usr/bin/psnup <OLD> -1176452778 1166527247 perl-tk /usr/bin/ptked <OLD> -1176452778 1175633785 pinentry-qt /usr/bin/pinentry-qt <OLD> -1176452778 1167289228 po-debconf /usr/bin/podebconf-display-po <OLD> -1176452778 1166190410 pinentry-curses /usr/bin/pinentry-curses <OLD> -1176452778 1166542133 a2ps /usr/bin/psmandup <OLD> -1176452777 1169039187 pcmanfm /usr/bin/pcmanfm <OLD> -1176452777 1166370937 pbuilder /usr/bin/pdebuild <OLD> -1176452777 1166527235 libparse-debianchangelog-perl /usr/bin/parsechangelog <OLD> -1176452777 1169582960 packagesearch /usr/bin/packagesearch <OLD> -1176452777 1166190412 patch /usr/bin/patch <OLD> -1176452777 1166980537 perl-doc /usr/bin/perldoc <OLD> -1176452777 1166190405 libpcre3 /usr/bin/pcretest <OLD> -1176452777 1171977060 libpango1.0-dev /usr/bin/pango-view <OLD> -1176452777 1166544984 libpaper-utils /usr/bin/paperconf <OLD> -1176452776 1166527004 sp /usr/bin/nsgmls <OLD> -1176452776 1173452014 unixodbc-bin /usr/bin/ODBCConfig <OLD> -1176452776 1171415994 gdal-bin /usr/bin/ogrtindex <OLD> -1176452776 1166527245 octave2.1 /usr/bin/octave2.1 <OLD> -1176452776 1166372906 odbcinst1debian1 /usr/bin/odbcinst <OLD> -1176452776 1172692921 liboil0.3 /usr/bin/oil-bugreport <OLD> -1176452776 1166190411 bin86 /usr/bin/objdump86 <OLD> -1176452775 1166190411 ftp /usr/bin/pftp <OLD> -1176452775 1166190402 gettext-base /usr/bin/ngettext <OLD> -1176452775 1173553215 policycoreutils /usr/bin/newrole <OLD> -1176452774 1166527253 mtr /usr/bin/mtr <OLD> -1176452774 1168601034 netcdf-bin /usr/bin/ncdump <OLD> -1176452774 1166190412 mpack /usr/bin/munpack <OLD> -1176452774 1173805996 nautilus-cd-burner /usr/bin/nautilus-cd-burner <OLD> -1176452774 1166190411 mtools /usr/bin/mxtar <OLD> -1176452773 1175768483 grub /usr/bin/mkbimage <OLD> -1176452773 1166493277 mixxx /usr/bin/mixxx <OLD> -1176452773 1166190407 mcrypt /usr/bin/mdecrypt <OLD> -1176452773 1166492130 mmv /usr/bin/mad <OLD> -1176452772 1176450674 lwatch /usr/bin/lwatch <OLD> -1176452772 1166853121 m17n-db /usr/bin/m17n-db <OLD> -1176452772 1166190412 mailx /usr/bin/mailx <OLD> -1176452772 1166190402 libnss-db /usr/bin/makedb <OLD> -1176452772 1171415965 libmagick9-dev /usr/bin/Magick-config <OLD> -1176452772 1170794026 cupsys-client /usr/bin/lpstat <OLD> -1176452772 1166527580 libwww-perl /usr/bin/GET <OLD> -1176452772 1166527244 memtest86+ /usr/bin/make-memtest86+-boot-floppy <OLD> -1176452772 1171415964 libmagick++9-dev /usr/bin/Magick++-config <OLD> -1176452771 1166372775 libtool /usr/bin/libtool <OLD> -1176452771 1166527230 libgnutls-dev /usr/bin/libgnutls-config <OLD> -1176452771 1168444996 link-grammar /usr/bin/link-grammar <OLD> -1176452771 1166527242 linda /usr/bin/linda <OLD> -1176452771 1167557338 libnet1-dev /usr/bin/libnet-config <OLD> -1176452771 1171415976 kdebase-bin /usr/bin/kxkb <OLD> -1176452771 1167896485 libglade2-dev /usr/bin/libglade-convert <OLD> -1176452771 1173870377 ldap-utils /usr/bin/ldapadd <OLD> -1176452770 1175633689 kwin /usr/bin/kwin <OLD> -1176452770 1171415975 kcontrol /usr/bin/krdb <OLD> -1176452770 1171415980 kdebase-kio-plugins /usr/bin/ktrash <OLD> -1176452770 1171415976 kdesktop /usr/bin/kwebdesktop <OLD> -1176452769 1175633742 kpdf /usr/bin/kpdf <OLD> -1176452769 1175633688 konsole /usr/bin/konsole <OLD> -1176452768 1175633741 kghostview /usr/bin/kghostview <OLD> -1176452768 1174603739 python-kid /usr/bin/kid <OLD> -1176452768 1175633686 kfind /usr/bin/kfind <OLD> -1176452768 1172692925 kino /usr/bin/kino <OLD> -1176452768 1171415974 kicker /usr/bin/kicker <OLD> -1176452768 1175633762 kdict /usr/bin/kdict <OLD> -1176452767 1169371501 console-common /usr/bin/kbd-config <OLD> -1176452767 1167188509 kaffeine /usr/bin/kaffeine <OLD> -1176452767 1169667653 kcachegrind /usr/bin/kcachegrind <OLD> -1176452766 1166527203 inkscape /usr/bin/inkview <OLD> -1176452766 1173553668 irssi /usr/bin/irssi <OLD> -1176452766 1166493272 jackd /usr/bin/jack_connect <OLD> -1176452766 1168510530 im-switch /usr/bin/im-switch <OLD> -1176452766 1166372906 unixodbc /usr/bin/iusql <OLD> -1176452766 1169982581 autoconf /usr/bin/ifnames <OLD> -1176452765 1166544983 libgstreamer0.8-0 /usr/bin/gst-register-0.8 <OLD> -1176452765 1169039183 gij /usr/bin/grmiregistry <OLD> -1176452765 1169026304 grass /usr/bin/grass <OLD> -1176452765 1175426291 apache2-utils /usr/bin/htpasswd <OLD> -1176452765 1168522526 help2man /usr/bin/help2man <OLD> -1176452765 1166541751 hanzim /usr/bin/hanzim <OLD> -1176452765 1169039183 gij-4.1 /usr/bin/grmiregistry-4.1 <OLD> -1176452765 1167557334 hdf5-tools /usr/bin/h5debug <OLD> -1176452765 1166372529 html2text /usr/bin/html2text <OLD> -1176452765 1166527581 gromit /usr/bin/gromit <OLD> -1176452765 1167896567 gnome-media /usr/bin/gstreamer-properties <OLD> -1176452764 1174287091 gobby /usr/bin/gobby <OLD> -1176452764 1166545029 gnuplot-nox /usr/bin/gnuplot <OLD> -1176452764 1166372952 gperf /usr/bin/gperf <OLD> -1176452763 1166527580 glademm /usr/bin/glademm-embed <OLD> -1176452763 1166190406 gnome-nettool /usr/bin/gnome-nettool <OLD> -1176452763 1173697180 git-core /usr/bin/git-pack-refs <OLD> -1176452763 1167557320 glade-gnome /usr/bin/glade-2 <OLD> -1176452763 1170283746 gmt /usr/bin/GMT <OLD> -1176452763 1172692908 gnome-about /usr/bin/gnome-about <OLD> -1176452763 1166190408 gnome-keyring-manager /usr/bin/gnome-keyring-manager <OLD> -1176452763 1172692910 gnome-menus /usr/bin/gnome-menu-spec-test <OLD> -1176452762 1173474008 libgtk2.0-dev /usr/bin/gdk-pixbuf-csource <OLD> -1176452762 1169252735 libgeos-dev /usr/bin/geos-config <OLD> -1176452761 1166853121 libfribidi0 /usr/bin/fribidi <OLD> -1176452761 1166190410 gcalctool /usr/bin/gnome-calculator <OLD> -1176452761 1175126572 ffmpeg /usr/bin/ffplay <OLD> -1176452761 1171415993 libgdal1-1.3.2-dev /usr/bin/gdal-config <OLD> -1176452761 1166190407 gconf-editor /usr/bin/gconf-editor <OLD> -1176452761 1171977059 libgda2-bin /usr/bin/gda-test <OLD> -1176452760 1167197446 ffmpeg2theora /usr/bin/ffmpeg2theora <OLD> -1176452760 1168883513 exiftran /usr/bin/exiftran <OLD> -1176452760 1174638054 evolution /usr/bin/evolution <OLD> -1176452760 1173697172 eog /usr/bin/eog <OLD> -1176452760 1169987090 libenchant1c2a /usr/bin/enchant <OLD> -1176452760 1166527569 festival /usr/bin/festival_client <OLD> -1176452760 1166527229 libesd0-dev /usr/bin/esd-config <OLD> -1176452760 1168883513 exif /usr/bin/exif <OLD> -1176452760 1166190411 esound /usr/bin/esd <OLD> -1176452759 1167185763 dvbsnoop /usr/bin/dvbsnoop <OLD> -1176452759 1166527023 emma /usr/bin/Emma <OLD> -1176452758 1166527001 diffstat /usr/bin/diffstat <OLD> -1176452758 1169982582 dictzip /usr/bin/dictzcat <OLD> -1176452758 1170968117 dialog /usr/bin/dialog <OLD> -1176452758 1166541750 dh-buildinfo /usr/bin/dh_buildinfo <OLD> -1176452758 1166526998 dia-gnome /usr/bin/dia-gnome <OLD> -1176452758 1168364125 dselect /usr/bin/dselect <OLD> -1176452758 1166526998 dia /usr/bin/dia-normal <OLD> -1176452758 1166190405 liblockfile1 /usr/bin/dotlockfile <OLD> -1176452757 1166526993 dadadodo /usr/bin/dadadodo <OLD> -1176452757 1166190408 dc /usr/bin/dc <OLD> -1176452757 1166526994 dbus-1-utils /usr/bin/dbus-monitor <OLD> -1176452757 1166526994 debtags /usr/bin/debtags <OLD> -1176452757 1166526993 darcs /usr/bin/darcs <OLD> -1176452757 1166541745 libdbi-perl /usr/bin/dbiproxy <OLD> -1176452757 1166526993 darcs-buildpackage /usr/bin/dbp-markdeb <OLD> -1176452756 1166526993 curl /usr/bin/curl <OLD> -1176452756 1166190406 libcroco3 /usr/bin/csslint-0.6 <OLD> -1176452756 1173474001 comerr-dev /usr/bin/compile_et <OLD> -1176452756 1174593896 cmake /usr/bin/ctest <OLD> -1176452756 1166526992 conglomerate /usr/bin/conglomerate <OLD> -1176452756 1166526992 connect-proxy /usr/bin/connect <OLD> -1176452755 1166370937 cdebootstrap /usr/bin/cdebootstrap <OLD> -1176452755 1166493253 btscanner /usr/bin/btscanner <OLD> -1176452755 1166493268 cadaver /usr/bin/cadaver <OLD> -1176452755 1166194139 bsh /usr/bin/bsh <OLD> -1176452755 1173647366 python-cheetah /usr/bin/cheetah <OLD> -1176452755 1168682724 bug-buddy /usr/bin/bug-buddy <OLD> -1176452755 1166493270 cappuccino /usr/bin/cappuccino <OLD> -1176452754 1166541750 automake /usr/bin/automake-1.10 <OLD> -1176452754 1166527227 libaudiofile-dev /usr/bin/audiofile-config <OLD> -1176452754 1166190410 bc /usr/bin/bc <OLD> -1176452754 1166493247 autotrace /usr/bin/autotrace <OLD> -1176452754 1166190402 bison /usr/bin/bison <OLD> -1176452754 1166527209 libarts1c2a /usr/bin/artswrapper <OLD> -1176452754 1171620539 boa-constructor /usr/bin/boa-constructor <OLD> -1176452753 1166492351 libadns1-bin /usr/bin/adnsresfilter <OLD> -1176452753 1166493228 apt-rdepends /usr/bin/apt-rdepends <OLD> -1176452753 1166527226 libakode2 /usr/bin/akodeplay <OLD> -1176452753 1166493217 apt-file /usr/bin/apt-file <OLD> -1176381709 1167557335 libhdf5-serial-dev /usr/include/H5Ppublic.h <OLD> -1176381708 1168601033 netcdfg-dev /usr/include/ncvalues.h <OLD> -1176240161 1172331617 linux-image-2.6.20.1enrico /boot/System.map-2.6.20.1enrico <OLD> -1176187919 1173870388 totem-xine /usr/bin/totem-video-thumbnailer <OLD> -1176107897 1175877396 toilet /usr/bin/figlet-toilet <OLD> -1175968220 1169991072 tftp /usr/bin/tftp <OLD> -1175939042 1166372774 libpod-simple-perl /usr/share/perl5/Pod/Simple/BlackBox.pm <OLD> -1175939042 1166372774 libpod-escapes-perl /usr/share/perl5/Pod/Escapes.pm <OLD> -1175936595 1171977056 klibc-utils /usr/lib/klibc/bin/kill <OLD> -1175768425 1175768448 libx11-dev /usr/lib/libX11.a <OLD> -1175720113 1175720149 kdelibs-data /usr/share/apps/dcopidlng/kalyptusCxxToDcopIDL.pm <OLD> -1175701618 1175699053 libmsat-dev /usr/include/msat/Image.h <OLD> -1175701618 1166373018 libpopt-dev /usr/include/popt.h <OLD> -1175699060 1175699053 libhrit-dev /usr/include/hrit/MSG_machine.h <OLD> -1175699052 1175699052 libgrib-dev /usr/lib/libgrib.a <OLD> -1175633925 1166527580 libxml-parser-perl /usr/lib/perl5/XML/Parser/Expat.pm <OLD> -1175633925 1175633668 libxml-sax-expat-perl /usr/share/perl5/XML/SAX/Expat.pm <OLD> -1175633925 1166527241 libxml-sax-perl /usr/bin/update-perl-sax-parsers <OLD> -1175633925 1166527241 libxml-namespacesupport-perl /usr/share/perl5/XML/NamespaceSupport.pm <OLD> -1175633632 1175633862 xli /usr/bin/xli <OLD> -1175593578 1166603946 libcnf-dev /usr/lib/libcnf.a <OLD> -1175353646 1175353594 libsdl1.2-dev /usr/include/SDL/close_code.h <OLD> -1175353646 1172692918 libglu1-mesa-dev /usr/include/GL/glu.h <OLD> -1175353646 1175353597 libsdl-mixer1.2-dev /usr/include/SDL/SDL_mixer.h <OLD> -1175353646 1175353594 libsdl-image1.2-dev /usr/include/SDL/SDL_image.h <OLD> -1175353646 1172692916 mesa-common-dev /usr/include/GL/glext.h <OLD> -1175353574 1175353595 libvorbis-dev /usr/lib/libvorbisfile.a <OLD> -1175353574 1175353592 libogg-dev /usr/include/ogg/os_types.h <OLD> -1175353574 1175353597 libsmpeg-dev /usr/lib/libsmpeg.a <OLD> -1175212670 1171415989 shared-mime-info /usr/bin/update-mime-database <OLD> -1175163877 1173202538 xchm /usr/bin/xchm <OLD> -1175005453 1166372906 unixodbc-dev /usr/include/sqlucode.h <OLD> -1174947050 1167109785 liblogfile-rotate-perl /usr/share/perl5/Logfile/Rotate.pm <OLD> -1174947050 1167109785 libconfhelper-perl /usr/share/perl5/ConfHelper.pm <OLD> -1174639562 1166372530 libcompress-zlib-perl /usr/lib/perl5/Compress/Zlib.pm <OLD> -1174637980 1166372773 libclass-accessor-perl /usr/share/perl5/Class/Accessor.pm <OLD> -1174488555 1166190411 strace /usr/bin/strace <OLD> -1174387948 1174388018 sound-juicer /usr/bin/sound-juicer <OLD> -1174316026 1167557339 libpcap0.7-dev /usr/include/net/bpf.h <OLD> -1174314249 1171415962 libsqlite0-dev /usr/include/sqlite.h <OLD> -1174314249 1173127613 libfontconfig1-dev /usr/include/fontconfig/fcfreetype.h <OLD> -1174314249 1166190620 python-numeric /usr/include/python2.4/Numeric/ufuncobject.h <OLD> -1174314249 1166527233 libsigc++-2.0-dev /usr/include/sigc++-2.0/sigc++/sigc++.h <OLD> -1174314249 1174236298 libgnomevfs2-dev /usr/include/gnome-vfs-module-2.0/libgnomevfs/gnome-vfs-pty.h <OLD> -1174314249 1168804322 python-numeric-ext /usr/include/python2.4/Numeric/ranlib.h <OLD> -1174314249 1167557336 liblcms1-dev /usr/include/icc34.h <OLD> -1174314249 1171977032 libexpat1-dev /usr/include/expat_config.h <OLD> -1174314249 1167557336 libjasper-1.701-dev /usr/include/jasper/jas_malloc.h <OLD> -1174314249 1170794018 sysvinit /usr/include/initreq.h <OLD> -1174314249 1166372774 libltdl3-dev /usr/include/ltdl.h <OLD> -1174314249 1166526982 x11proto-xinerama-dev /usr/include/X11/extensions/panoramiXext.h <OLD> -1174314249 1166527229 libxml2-dev /usr/include/libxml2/libxml/globals.h <OLD> -1174314249 1169582337 libmng-dev /usr/include/libmng.h <OLD> -1174314249 1166980514 libgnome-keyring-dev /usr/include/gnome-keyring-1/gnome-keyring.h <OLD> -1174314249 1166542751 libxapian-dev /usr/include/xapian/valueiterator.h <OLD> -1174314249 1168076530 libtiff4-dev /usr/include/tiffvers.h <OLD> -1174314249 1166527234 libjpeg62-dev /usr/include/jpegint.h <OLD> -1174314249 1166526980 x11proto-xext-dev /usr/include/X11/extensions/dpms.h <OLD> -1174314249 1172691933 libapt-pkg-dev /usr/include/apt-pkg/extracttar.h <OLD> -1174314249 1166526980 x11proto-kb-dev /usr/include/X11/extensions/XKBstr.h <OLD> -1174314249 1166493207 python-pygame /usr/include/python2.4/pygame/font.h <OLD> -1174314249 1166527228 libgnomecanvas2-dev /usr/include/libgnomecanvas-2.0/libgnomecanvas/gnome-canvas-polygon.h <OLD> -1174314249 1166527229 liblzo-dev /usr/include/lzo2a.h <OLD> -1174314249 1166527227 libdbus-1-dev /usr/include/dbus-1.0/dbus/dbus-pending-call.h <OLD> -1174314249 1168445049 liblink-grammar4-dev /usr/include/link-grammar/link-features.h <OLD> -1174314249 1166527228 libcairo2-dev /usr/include/cairo/cairo.h <OLD> -1174314249 1167557334 libbz2-dev /usr/include/bzlib.h <OLD> -1174314249 1169371528 cryptsetup /usr/include/libcryptsetup.h <OLD> -1174314249 1166526981 x11proto-render-dev /usr/include/X11/extensions/render.h <OLD> -1174314249 1172692915 libgl1-mesa-dev /usr/include/GL/glx.h <OLD> -1174314249 1166526981 x11proto-fixes-dev /usr/include/X11/extensions/xfixeswire.h <OLD> -1174314249 1171415983 libpq-dev /usr/include/postgresql/mb/pg_wchar.h <OLD> -1174296996 1170426154 python-scipy /usr/share/pycentral/python-scipy/site-packages/scipy/weave/blitz/blitz/bench.h <OLD> -1173870868 1166527235 libmailtools-perl /usr/share/perl5/Mail/Mailer/sendmail.pm <OLD> -1173870819 1166544982 libclass-methodmaker-perl /usr/lib/perl5/Class/MethodMaker.pm <OLD> -1173870819 1172192955 libio-stringy-perl /usr/share/perl5/IO/WrapTie.pm <OLD> -1173870819 1166544983 libgnupg-interface-perl /usr/share/perl5/GnuPG/Options.pm <OLD> -1173870818 1166545119 libtext-template-perl /usr/share/perl5/Text/Template.pm <OLD> -1173870395 1173870361 ca-certificates /usr/sbin/update-ca-certificates <OLD> -1173866940 1166493253 bsdgames /usr/games/morse <OLD> -1173697155 1173697160 radeontool /usr/sbin/radeontool <OLD> -1173553227 1166190615 lvm-common /usr/sbin/lvm-bin-scan <OLD> -1173481316 1173474010 libgtk2.0-bin /usr/sbin/update-gdkpixbuf-loaders <OLD> -1173474004 1173474045 xvnc4viewer /usr/bin/xvnc4viewer <OLD> -1173374741 1173374759 libpg-perl /usr/share/perl5/Pg.pm <OLD> -1173374741 1173374759 libdbd-pg-perl /usr/lib/perl5/DBD/Pg.pm <OLD> -1173215667 1173044869 rrootage /usr/games/rrootage <OLD> -1173032743 1171415983 perlmagick /usr/lib/perl5/Image/Magick.pm <OLD> -1173000088 1173000089 mount /sbin/swapoff <OLD> -1172968089 1172967534 bygfoot /usr/games/bygfoot <OLD> -1172822444 1166190613 sgml-base /usr/sbin/install-sgmlcatalog <OLD> -1172692896 1172692912 hdparm /sbin/hdparm <OLD> -1172326759 1168621101 source-highlight /usr/bin/source-highlight <OLD> -1172326712 1172327116 ant /usr/bin/ant <OLD> -1172326712 1172326731 java-common /usr/sbin/update-java-alternatives <OLD> -1172192928 1172192954 portmap /sbin/pmap_set <OLD> -1171977263 1171977062 libpango1.0-common /usr/sbin/update-pangox-aliases <OLD> -1171977005 1171977213 ipw3945d /sbin/ipw3945d <OLD> -1171977005 1171977051 busybox /bin/busybox <OLD> -1171416038 1171415990 ssl-cert /usr/sbin/make-ssl-cert <OLD> -1170967425 1170084318 xsane /usr/bin/xsane <OLD> -1170892768 1170892769 dash /bin/dash <OLD> -1170877521 1170877544 ceferino /usr/games/ceferinoeditor <OLD> -1170794020 1170794021 initscripts /sbin/fsck.nfs <OLD> -1170637598 1170637641 xdelta /usr/bin/xdelta <OLD> -1170637598 1170637623 gnome-games /usr/games/iagno <OLD> -1169759887 1169667651 angrydd /usr/games/angrydd <OLD> -1169252753 1166527581 tk8.4 /usr/bin/wish8.4 <OLD> -1168896609 1168896624 libimage-base-bundle-perl /usr/share/perl5/Image/Xpm.pm <OLD> -1168608457 1168608460 xmms-shell /usr/bin/xmms-shell <OLD> -1168607980 1168607993 xmms-midi /usr/lib/xmms/Input/libmid.a <OLD> -1168377698 1168377699 libnet-ldap-perl /usr/share/perl5/LWP/Protocol/ldap.pm <OLD> -1168377698 1168377699 libconvert-asn1-perl /usr/share/perl5/Convert/ASN1/_encode.pm <OLD> -1168201952 1166190366 libgtk2-perl /usr/lib/perl5/Gtk2/SimpleMenu.pm <OLD> -1168201952 1168199689 tinyca /usr/bin/tinyca2 <OLD> -1168201951 1166190366 libcairo-perl /usr/lib/perl5/Cairo.pm <OLD> -1167557302 1167557318 glade-common /usr/share/doc/glade-common/examples/editor/acconfig.h <OLD> -1167291089 1167289232 frozen-bubble /usr/share/perl5/fb_stuff.pm <OLD> -1167285834 1167285834 liberror-perl /usr/share/perl5/Error.pm <OLD> -1166980509 1166980543 tesseract-ocr /usr/bin/tesseract <OLD> -1166545113 1166545116 libpango1-ruby /usr/lib/ruby/1.8/x86_64-linux/rbpangoversion.h <OLD> -1166545113 1166545116 libatk1-ruby /usr/lib/ruby/1.8/x86_64-linux/rbatk.h <OLD> -1166545113 1166545115 libglib2-ruby /usr/lib/ruby/1.8/x86_64-linux/rbgobject.h <OLD> -1166545113 1166545116 libgtk2-ruby /usr/lib/ruby/1.8/x86_64-linux/rbgtk.h <OLD> -1166545113 1166545115 libart2-ruby /usr/lib/ruby/1.8/x86_64-linux/rbart.h <OLD> -1166545113 1166545116 libcairo-ruby1.8 /usr/lib/ruby/1.8/x86_64-linux/rb_cairo.h <OLD> -1166544981 1166544990 python-wxglade /usr/bin/wxglade <OLD> -1166544981 1166545007 stow /usr/bin/stow <OLD> -1166544981 1166545008 tzwatch /usr/bin/tzwatch <OLD> -1166544981 1166545008 trickle /usr/bin/trickle <OLD> -1166544981 1166545008 units /usr/bin/units <OLD> -1166544981 1166545007 swaks /usr/bin/swaks <OLD> -1166544981 1166544984 libmd5-perl /usr/share/perl5/MD5.pm <OLD> -1166544981 1166544983 libgtk2-gladexml-perl /usr/lib/perl5/Gtk2/GladeXML.pm <OLD> -1166541744 1166541750 tcl8.3 /usr/bin/tclsh8.3 <OLD> -1166541744 1166541744 libnet-daemon-perl /usr/share/perl5/Net/Daemon.pm <OLD> -1166541744 1166541760 dopewars /usr/games/dopewars <OLD> -1166541744 1166541745 libplrpc-perl /usr/share/perl5/Bundle/PlRPC.pm <OLD> -1166541744 1166541751 tk8.3 /usr/bin/wish8.3 <OLD> -1166527567 1166527570 doxygen-doc /usr/share/doc/doxygen/examples/structcmd.h <OLD> -1166527354 1166527241 libxml-libxml-common-perl /usr/lib/perl5/XML/LibXML/Common.pm <OLD> -1166527354 1166527241 libxml-libxml-perl /usr/lib/perl5/XML/LibXML/Number.pm <OLD> -1166526979 1166526982 libxi-dev /usr/lib/libXi.a <OLD> -1166526979 1166527249 stl-manual /usr/share/doc/stl-manual/html/vector.h <OLD> -1166526979 1166527235 libhtml-format-perl /usr/share/perl5/HTML/FormatPS.pm <OLD> -1166526979 1166527032 libhtml-tagset-perl /usr/share/perl5/HTML/Tagset.pm <OLD> -1166526979 1166527234 libfont-afm-perl /usr/share/perl5/Font/Metrics/Courier.pm <OLD> -1166526979 1166527250 tagcolledit /usr/bin/tagcolledit <OLD> -1166493179 1166493216 libconfig-file-perl /usr/share/perl5/Config/File.pm <OLD> -1166491978 1166491979 libsasl2-2 /usr/lib/sasl2/libsasldb.a <OLD> -1166491978 1166491979 libsasl2-modules /usr/lib/sasl2/liblogin.a <OLD> -1166372771 1166372772 libalgorithm-annotate-perl /usr/share/perl5/Algorithm/Annotate.pm <OLD> -1166372771 1166372773 libio-string-perl /usr/share/perl5/IO/String.pm <OLD> -1166372771 1166372777 subversion-tools /usr/bin/svn_apply_autoprops <OLD> -1166372771 1166372773 libfreezethaw-perl /usr/share/perl5/FreezeThaw.pm <OLD> -1166372771 1166372775 libtext-diff-perl /usr/share/perl5/Text/Diff/Table.pm <OLD> -1166372771 1166372772 libalgorithm-diff-perl /usr/share/perl5/Algorithm/Diff.pm <OLD> -1166372771 1166372777 python-subversion /usr/bin/svnshell <OLD> -1166372771 1166372773 libextutils-autoinstall-perl /usr/share/perl5/ExtUtils/AutoInstall.pm <OLD> -1166372522 1166372530 libmail-sendmail-perl /usr/share/perl5/Mail/Sendmail.pm <OLD> -1166370841 1166370849 resolvconf /sbin/resolvconf <OLD> -1166195126 1166195126 xfsdump /usr/sbin/xfsrestore <OLD> -1166191394 1166191399 dmsetup /sbin/dmsetup <OLD> -1166191159 1166190413 libconfig-inifiles-perl /usr/share/perl5/Config/IniFiles.pm <OLD> -1166190917 1166190542 libgnome2-canvas-perl /usr/share/doc/libgnome2-canvas-perl/examples/canvas-primitives.pm.gz <OLD> -1166190887 1166190615 libpam-runtime /usr/sbin/pam_getenv <OLD> -1166190887 1166190615 libpam-modules /usr/sbin/pam_tally <OLD> -1166190887 1166190615 base-passwd /usr/sbin/update-passwd <OLD> -1166190886 1166190615 libident /usr/sbin/in.identtestd <OLD> -1166190885 1166190615 net-tools /usr/sbin/arp <OLD> -1166190884 1166190366 libgnome2-perl /usr/lib/perl5/Gnome2.pm <OLD> -1166190884 1166190366 libgnome2-vfs-perl /usr/lib/perl5/Gnome2/VFS.pm <OLD> -1166190775 1166190411 type-handling /usr/bin/type-handling <OLD> -1166190775 1166190404 tcl8.4 /usr/bin/tclsh8.4 <OLD> -1166190775 1166190407 language-env /usr/bin/tklanguage <OLD> -1166190775 1166190406 libxml2-utils /usr/bin/xmllint <OLD> -1166190775 1166190399 tcsh /bin/tcsh <OLD> -1166190775 1166190401 time /usr/bin/time <OLD> -1166190775 1166190410 twm /usr/bin/twm <OLD> -1166111683 1166111796 pcmciautils /sbin/lspcmcia <OLD> -1166111683 1166111684 iptables /sbin/iptables-restore <OLD> -1166111669 1166111669 hostname /bin/dnsdomainname <OLD> -1166088950 1166088395 makedev /sbin/MAKEDEV <OLD> -1166088684 1166088872 wireless-tools /sbin/iwconfig <OLD> -1166088684 1166088719 bzip2 /bin/bzcmp <OLD> -1166088684 1166088703 powermgmt-base /usr/bin/on_ac_power <OLD> -1166088409 1166088411 ed /usr/bin/red <OLD> -1166088409 1166088411 ifupdown /sbin/ifup <OLD> -0 0 notification-daemon <NOFILES> -0 0 libslp1 <NOFILES> -0 0 python-rpy <NOFILES> -0 0 libgtksourceview-common <NOFILES> -0 0 compiz-gnome <NOFILES> -0 0 libvte-ruby <NOFILES> -0 0 libgphoto2-2 <NOFILES> -0 0 xserver-xorg-input-kbd <NOFILES> -0 0 libvorbis0a <NOFILES> -0 0 python-gnome2 <NOFILES> -0 0 python-dateutil <NOFILES> -0 0 libvorbisenc2 <NOFILES> -0 0 libgstreamer-plugins0.8-0 <NOFILES> -0 0 libgnome-media0 <NOFILES> -0 0 libxcursor1 <NOFILES> -0 0 libcln4 <NOFILES> -0 0 libsdl1.2debian <NOFILES> -0 0 libscim8c2a <NOFILES> -0 0 xserver-xorg-video-s3 <NOFILES> -0 0 python-sqlite <NOFILES> -0 0 gedit-common <NOFILES> -0 0 libdbus-glib-1-2 <NOFILES> -0 0 python-rpy-doc <NOFILES> -0 0 proj-ps-doc <NOFILES> -0 0 xsane-common <NOFILES> -0 0 libgl1-mesa-dri <NOFILES> -0 0 libx11-6 <NOFILES> -0 0 libwnck18 <NOFILES> -0 0 libxklavier10 <NOFILES> -0 0 sun-java5-jre <NOFILES> -0 0 libtasn1-3 <NOFILES> -0 0 bochsbios <NOFILES> -0 0 xmms-speex <NOFILES> -0 0 svgalibg1 <NOFILES> -0 0 sgml-data <NOFILES> -0 0 libgtkmm-2.4-1c2a <NOFILES> -0 0 libboost-regex1.33.1 <NOFILES> -0 0 texlive-doc-base <NOFILES> -0 0 libedataserverui1.2-6 <NOFILES> -0 0 gcj-4.1-base <NOFILES> -0 0 r-cran-kernsmooth <NOFILES> -0 0 libdballe-bufrex-doc <NOFILES> -0 0 python-gtk2-doc <NOFILES> -0 0 libwmf0.2-7 <NOFILES> -0 0 librsvg2-ruby <NOFILES> -0 0 libgnomecupsui1.0-1c2a <NOFILES> -0 0 proll <NOFILES> -0 0 libgcj7-0 <NOFILES> -0 0 libcfitsio2 <NOFILES> -0 0 xfonts-scalable <NOFILES> -0 0 xserver-xorg <NOFILES> -0 0 libxft2 <NOFILES> -0 0 libmagick++9c2a <NOFILES> -0 0 gnome-media-common <NOFILES> -0 0 libasound2 <NOFILES> -0 0 libnotify1 <NOFILES> -0 0 libcdio6 <NOFILES> -0 0 python-cherrypy <NOFILES> -0 0 libwpd8c2a <NOFILES> -0 0 libdb4.3 <NOFILES> -0 0 libgsf-1-common <NOFILES> -0 0 libxmu6 <NOFILES> -0 0 libnss3-0d <NOFILES> -0 0 libcupsys2 <NOFILES> -0 0 swig-doc <NOFILES> -0 0 debconf-i18n <NOFILES> -0 0 openoffice.org-l10n-en-gb <NOFILES> -0 0 libdballe-msg3 <NOFILES> -0 0 scim-chewing <NOFILES> -0 0 libdevmapper1.02 <NOFILES> -0 0 libfreetype6 <NOFILES> -0 0 texlive-pictures <NOFILES> -0 0 openbox-themes <NOFILES> -0 0 discover1-data <NOFILES> -0 0 libopal-2.2.0 <NOFILES> -0 0 libmagick9 <NOFILES> -0 0 gtk2-engines <NOFILES> -0 0 console-data <NOFILES> -0 0 libxv1 <NOFILES> -0 0 libufsparse <NOFILES> -0 0 libaprutil1 <NOFILES> -0 0 libfltk1.1 <NOFILES> -0 0 libmdbtools <NOFILES> -0 0 libjline-java <NOFILES> -0 0 libchewing3 <NOFILES> -0 0 python-ooolib <NOFILES> -0 0 cups-pdf <NOFILES> -0 0 libmcrypt4 <NOFILES> -0 0 openoffice.org-l10n-it <NOFILES> -0 0 openoffice.org-java-common <NOFILES> -0 0 gstreamer0.10-esd <NOFILES> -0 0 libslang2 <NOFILES> -0 0 docbook-xsl <NOFILES> -0 0 iceweasel-webdeveloper <NOFILES> -0 0 libportaudio0 <NOFILES> -0 0 libpci2 <NOFILES> -0 0 libgdal-doc <NOFILES> -0 0 libreadline-ruby1.8 <NOFILES> -0 0 gnome-applets-data <NOFILES> -0 0 python-syck <NOFILES> -0 0 python-gmenu <NOFILES> -0 0 libgnome32 <NOFILES> -0 0 fftw2 <NOFILES> -0 0 libgnome-window-settings1 <NOFILES> -0 0 libgnomecanvas2-ruby <NOFILES> -0 0 libpango1.0-0 <NOFILES> -0 0 libgeos-c1 <NOFILES> -0 0 libsdl-pango1 <NOFILES> -0 0 tesseract-ocr-data <NOFILES> -0 0 openhackware <NOFILES> -0 0 libnetcdf3 <NOFILES> -0 0 libtotem-plparser1 <NOFILES> -0 0 c++-annotations-txt <NOFILES> -0 0 python-paste <NOFILES> -0 0 xserver-xorg-video-voodoo <NOFILES> -0 0 gcc-3.3-base <NOFILES> -0 0 gimp-resynthesizer <NOFILES> -0 0 gstreamer0.10-ffmpeg <NOFILES> -0 0 tipa <NOFILES> -0 0 python-celementtree <NOFILES> -0 0 xserver-xorg-video-vga <NOFILES> -0 0 libdballepp3 <NOFILES> -0 0 myspell-en-us <NOFILES> -0 0 xserver-xorg-video-cirrus <NOFILES> -0 0 guile-1.6-libs <NOFILES> -0 0 developers-reference <NOFILES> -0 0 libkadm55 <NOFILES> -0 0 metacity-common <NOFILES> -0 0 libxul-common <NOFILES> -0 0 grass-doc <NOFILES> -0 0 python-gtk2 <NOFILES> -0 0 libtool-doc <NOFILES> -0 0 libapache2-mod-php5 <NOFILES> -0 0 libgda2-3 <NOFILES> -0 0 libdb4.4 <NOFILES> -0 0 libflac7 <NOFILES> -0 0 libldap2 <NOFILES> -0 0 libgnomemm1.2-9c2 <NOFILES> -0 0 libsexy2 <NOFILES> -0 0 libvisual-0.4-0 <NOFILES> -0 0 libglib2.0-0 <NOFILES> -0 0 libglu1-mesa <NOFILES> -0 0 libvte4 <NOFILES> -0 0 c++-annotations-latex <NOFILES> -0 0 libgnomesupport0 <NOFILES> -0 0 tomatoes-data <NOFILES> -0 0 compiz-plugins <NOFILES> -0 0 libgpgme11 <NOFILES> -0 0 libmng1 <NOFILES> -0 0 ttf-arphic-ukai <NOFILES> -0 0 libxosd2 <NOFILES> -0 0 python-uno <NOFILES> -0 0 libpaper1 <NOFILES> -0 0 ncurses-base <NOFILES> -0 0 libxul0d <NOFILES> -0 0 python-debian <NOFILES> -0 0 gstreamer0.10-plugins-good <NOFILES> -0 0 libgtksourceview1.0-0 <NOFILES> -0 0 libice6 <NOFILES> -0 0 glibc-doc-reference <NOFILES> -0 0 tzdata <NOFILES> -0 0 libattr1 <NOFILES> -0 0 freepats <NOFILES> -0 0 libgtkspell0 <NOFILES> -0 0 libgdal1-1.3.2 <NOFILES> -0 0 libgail-common <NOFILES> -0 0 gimp-python <NOFILES> -0 0 libgutenprint2 <NOFILES> -0 0 libntfs9 <NOFILES> -0 0 libexchange-storage1.2-1 <NOFILES> -0 0 libnetcdf++3 <NOFILES> -0 0 libdirectfb-0.9-25 <NOFILES> -0 0 libiso9660-4 <NOFILES> -0 0 iceweasel-l10n-it <NOFILES> -0 0 libxtst6 <NOFILES> -0 0 r-cran-sandwich <NOFILES> -0 0 postgresql-contrib <NOFILES> -0 0 libgnutls13 <NOFILES> -0 0 gimp-gap <NOFILES> -0 0 xserver-xorg-video-mga <NOFILES> -0 0 dia-common <NOFILES> -0 0 libxkbfile1 <NOFILES> -0 0 libogdi3.2 <NOFILES> -0 0 gimp-print <NOFILES> -0 0 autoconf-doc <NOFILES> -0 0 libgnomevfs2-common <NOFILES> -0 0 readline-common <NOFILES> -0 0 librsvg2-2 <NOFILES> -0 0 libscrollkeeper0 <NOFILES> -0 0 libotf0 <NOFILES> -0 0 libjasper-1.701-1 <NOFILES> -0 0 libdballe-msg-doc <NOFILES> -0 0 libvorbisfile3 <NOFILES> -0 0 libpoppler0c2-glib <NOFILES> -0 0 ibritish <NOFILES> -0 0 linux-doc-2.6.21 <NOFILES> -0 0 python-ctypes <NOFILES> -0 0 liba52-0.7.4 <NOFILES> -0 0 festvox-suopuhe-common <NOFILES> -0 0 python-webpy <NOFILES> -0 0 python-sip4 <NOFILES> -0 0 libgnomecanvas2-common <NOFILES> -0 0 python-vobject <NOFILES> -0 0 xserver-xorg-video-tseng <NOFILES> -0 0 gnome-mime-data <NOFILES> -0 0 festvox-italp16k <NOFILES> -0 0 libgnomeprint2.2-data <NOFILES> -0 0 toilet-fonts <NOFILES> -0 0 python-numpy-ext <NOFILES> -0 0 librsvg2-common <NOFILES> -0 0 libdm0 <NOFILES> -0 0 libmetacity0 <NOFILES> -0 0 libgnome2-common <NOFILES> -0 0 libdv4 <NOFILES> -0 0 libsmpeg0 <NOFILES> -0 0 libsane <NOFILES> -0 0 xapian-examples <NOFILES> -0 0 libsemanage1 <NOFILES> -0 0 libgnomecups1.0-1 <NOFILES> -0 0 xserver-xorg-video-ati <NOFILES> -0 0 gnome-core <NOFILES> -0 0 libsqliteodbc <NOFILES> -0 0 ant-optional <NOFILES> -0 0 python-flup <NOFILES> -0 0 lvm2 <NOFILES> -0 0 lapack3 <NOFILES> -0 0 libpostproc0d <NOFILES> -0 0 qalculate <NOFILES> -0 0 xserver-xorg-video-rendition <NOFILES> -0 0 freeglut3 <NOFILES> -0 0 libklibc <NOFILES> -0 0 manpages-it <NOFILES> -0 0 libsm6 <NOFILES> -0 0 cpp-4.1-doc <NOFILES> -0 0 libapm1 <NOFILES> -0 0 postfix-doc <NOFILES> -0 0 adept-common <NOFILES> -0 0 libpisock9 <NOFILES> -0 0 libshout3 <NOFILES> -0 0 libsdl1.2debian-alsa <NOFILES> -0 0 python-extractor <NOFILES> -0 0 libsdl-gfx1.2-4 <NOFILES> -0 0 libstdc++5 <NOFILES> -0 0 r-cran-abind <NOFILES> -0 0 libgnomeprintui2.2-common <NOFILES> -0 0 r-cran-rcmdr <NOFILES> -0 0 libt1-5 <NOFILES> -0 0 libglitz-glx1 <NOFILES> -0 0 festvox-itapc16k <NOFILES> -0 0 xorg <NOFILES> -0 0 libbeecrypt6 <NOFILES> -0 0 link-grammar-dictionaries-en <NOFILES> -0 0 libraw1394-8 <NOFILES> -0 0 lesstif2 <NOFILES> -0 0 libtiffxx0c2 <NOFILES> -0 0 libvformat1 <NOFILES> -0 0 python-crypto <NOFILES> -0 0 libchewing3-data <NOFILES> -0 0 gcc-3.4-base <NOFILES> -0 0 libgstreamer-plugins-base0.10-0 <NOFILES> -0 0 python-bluez <NOFILES> -0 0 evolution-data-server <NOFILES> -0 0 python-cairo <NOFILES> -0 0 base-files <NOFILES> -0 0 sqlite3-doc <NOFILES> -0 0 gnome-user-guide <NOFILES> -0 0 libkrb53 <NOFILES> -0 0 gnome-panel-data <NOFILES> -0 0 libhal1 <NOFILES> -0 0 libaudiofile0 <NOFILES> -0 0 libexif-ruby1.8 <NOFILES> -0 0 texlive-base <NOFILES> -0 0 r-cran-mvtnorm <NOFILES> -0 0 festvox-kallpc16k <NOFILES> -0 0 r-cran-strucchange <NOFILES> -0 0 libdebian-installer-extra4 <NOFILES> -0 0 libdc1394-13 <NOFILES> -0 0 libpq4 <NOFILES> -0 0 xserver-xorg-input-evdev <NOFILES> -0 0 r-cran-foreign <NOFILES> -0 0 libkonq4 <NOFILES> -0 0 vgabios <NOFILES> -0 0 python-all <NOFILES> -0 0 qgis-plugin-grass <NOFILES> -0 0 libmpfr1 <NOFILES> -0 0 lmodern <NOFILES> -0 0 python-at-spi <NOFILES> -0 0 gmt-tutorial <NOFILES> -0 0 libgnomevfs2-ruby <NOFILES> -0 0 libkcddb1 <NOFILES> -0 0 libaspell15 <NOFILES> -0 0 libmsat0 <NOFILES> -0 0 libxtrap6 <NOFILES> -0 0 libextractor-plugins <NOFILES> -0 0 libgtkhtml2-ruby <NOFILES> -0 0 python-mysqldb <NOFILES> -0 0 libaudio2 <NOFILES> -0 0 xmms-mad <NOFILES> -0 0 openoffice.org-evolution <NOFILES> -0 0 libevent1 <NOFILES> -0 0 postgresql-client <NOFILES> -0 0 librecode0 <NOFILES> -0 0 gcc-4.1-base <NOFILES> -0 0 libstartup-notification0 <NOFILES> -0 0 libxpm4 <NOFILES> -0 0 doc-linux-text <NOFILES> -0 0 gnome-desktop-data <NOFILES> -0 0 libpanel-applet2-0 <NOFILES> -0 0 libobby-0.4-0 <NOFILES> -0 0 libgnomeui-0 <NOFILES> -0 0 festvox-suopuhe-lj <NOFILES> -0 0 libnautilus-extension1 <NOFILES> -0 0 texlive-generic-recommended <NOFILES> -0 0 texlive-latex-recommended <NOFILES> -0 0 libcurl3 <NOFILES> -0 0 libnet1 <NOFILES> -0 0 libgtop2-7 <NOFILES> -0 0 wamerican <NOFILES> -0 0 r-cran-relimp <NOFILES> -0 0 xmms-xf86audio <NOFILES> -0 0 openoffice.org-core <NOFILES> -0 0 libbind9-30 <NOFILES> -0 0 libdmx1 <NOFILES> -0 0 gnome-netstatus-applet <NOFILES> -0 0 python-roman <NOFILES> -0 0 texpower <NOFILES> -0 0 libmagic1 <NOFILES> -0 0 libsvn1 <NOFILES> -0 0 libtiff4 <NOFILES> -0 0 python-profiler <NOFILES> -0 0 linux-image-2.6-amd64 <NOFILES> -0 0 ncurses-term <NOFILES> -0 0 libxau6 <NOFILES> -0 0 dict-bouvier <NOFILES> -0 0 libxcomposite1 <NOFILES> -0 0 libieee1284-3 <NOFILES> -0 0 python-semanage <NOFILES> -0 0 libqalculate3 <NOFILES> -0 0 libgconf2-4 <NOFILES> -0 0 libmpeg2-4 <NOFILES> -0 0 xserver-xorg-video-fbdev <NOFILES> -0 0 php5-common <NOFILES> -0 0 libgmp3c2 <NOFILES> -0 0 iceweasel-gnome-support <NOFILES> -0 0 prosper <NOFILES> -0 0 libsdl-console <NOFILES> -0 0 scim-modules-socket <NOFILES> -0 0 libgpmg1 <NOFILES> -0 0 libxfont1 <NOFILES> -0 0 netbase <NOFILES> -0 0 libdjvulibre15 <NOFILES> -0 0 xfonts-base <NOFILES> -0 0 autoconf-archive <NOFILES> -0 0 python-dev <NOFILES> -0 0 libebook1.2-5 <NOFILES> -0 0 festvox-suopuhe-mv <NOFILES> -0 0 libgnomevfs2-extra <NOFILES> -0 0 gnome-terminal-data <NOFILES> -0 0 xserver-xorg-input-all <NOFILES> -0 0 iceweasel-dom-inspector <NOFILES> -0 0 alsa-base <NOFILES> -0 0 libpth20 <NOFILES> -0 0 python-matplotlib-data <NOFILES> -0 0 libpcap0.7 <NOFILES> -0 0 postgresql-doc <NOFILES> -0 0 dia-libs <NOFILES> -0 0 libgnomevfs2-0 <NOFILES> -0 0 libcucul0 <NOFILES> -0 0 libstdc++6-4.1-doc <NOFILES> -0 0 libgphoto2-port0 <NOFILES> -0 0 debian-policy <NOFILES> -0 0 python-numpy-doc <NOFILES> -0 0 lsb-base <NOFILES> -0 0 libisccc0 <NOFILES> -0 0 gmt-coast-low <NOFILES> -0 0 python-apt <NOFILES> -0 0 xaw3dg <NOFILES> -0 0 c++-annotations-pdf <NOFILES> -0 0 docbook-xsl-doc <NOFILES> -0 0 libjaxp1.3-java <NOFILES> -0 0 gnome-cards-data <NOFILES> -0 0 libmpcdec3 <NOFILES> -0 0 gimp-data-extras <NOFILES> -0 0 ttf-dejavu <NOFILES> -0 0 evolution-data-server-common <NOFILES> -0 0 libbluetooth2 <NOFILES> -0 0 texlive-common <NOFILES> -0 0 libdballe-db3 <NOFILES> -0 0 libconsole <NOFILES> -0 0 libodbcinstq1c2 <NOFILES> -0 0 libavformat0d <NOFILES> -0 0 libgnome-menu2 <NOFILES> -0 0 xlibmesa-gl-dev <NOFILES> -0 0 libspeex1 <NOFILES> -0 0 libqt3-i18n <NOFILES> -0 0 libxalan2-java <NOFILES> -0 0 automake1.10-doc <NOFILES> -0 0 libgtkglext1-ruby <NOFILES> -0 0 libbonoboui2-common <NOFILES> -0 0 libvte-common <NOFILES> -0 0 libgsl0 <NOFILES> -0 0 libisc11 <NOFILES> -0 0 python-sqlalchemy <NOFILES> -0 0 libss2 <NOFILES> -0 0 libmad0 <NOFILES> -0 0 libsvga1 <NOFILES> -0 0 libncurses5 <NOFILES> -0 0 libept0 <NOFILES> -0 0 libsepol1 <NOFILES> -0 0 python-selinux <NOFILES> -0 0 libart-2.0-2 <NOFILES> -0 0 libplot2c2 <NOFILES> -0 0 iceweasel-greasemonkey <NOFILES> -0 0 libosp5 <NOFILES> -0 0 texlive-fonts-recommended <NOFILES> -0 0 odbc-postgresql <NOFILES> -0 0 libtheora0 <NOFILES> -0 0 libnfsidmap2 <NOFILES> -0 0 python-qtext <NOFILES> -0 0 xfonts-100dpi <NOFILES> -0 0 manpages <NOFILES> -0 0 libvolume-id0 <NOFILES> -0 0 libdballe-db-doc <NOFILES> -0 0 gcc-doc <NOFILES> -0 0 libqt4-gui <NOFILES> -0 0 libblkid1 <NOFILES> -0 0 libxss1 <NOFILES> -0 0 libruby1.8 <NOFILES> -0 0 libxine1 <NOFILES> -0 0 xserver-xorg-video-cyrix <NOFILES> -0 0 fast-user-switch-applet <NOFILES> -0 0 libmpich1.0c2 <NOFILES> -0 0 libgtop2-common <NOFILES> -0 0 mapserver-doc <NOFILES> -0 0 gnome-themes <NOFILES> -0 0 frozen-bubble-data <NOFILES> -0 0 intltool-debian <NOFILES> -0 0 libsdl-mixer1.2 <NOFILES> -0 0 libextractor1c2a <NOFILES> -0 0 python-pysqlite2 <NOFILES> -0 0 libgpg-error0 <NOFILES> -0 0 libgksuui1.0-1 <NOFILES> -0 0 libopengl-ruby <NOFILES> -0 0 python-gammu <NOFILES> -0 0 postgresql <NOFILES> -0 0 libarchive1 <NOFILES> -0 0 libflac++5 <NOFILES> -0 0 libxinerama1 <NOFILES> -0 0 libedataserver1.2-7 <NOFILES> -0 0 c++-annotations-ps <NOFILES> -0 0 eric-api-files <NOFILES> -0 0 python-gnome2-desktop <NOFILES> -0 0 graphviz-doc <NOFILES> -0 0 r-cran-lattice <NOFILES> -0 0 gconf2-common <NOFILES> -0 0 manpages-dev <NOFILES> -0 0 libgdk-pixbuf2-ruby <NOFILES> -0 0 libgnomecanvas2-0 <NOFILES> -0 0 xserver-xorg-video-sis <NOFILES> -0 0 r-doc-info <NOFILES> -0 0 libsmbclient <NOFILES> -0 0 gmt-examples <NOFILES> -0 0 libdrm2 <NOFILES> -0 0 libmusicbrainz4c2a <NOFILES> -0 0 python-pycurl <NOFILES> -0 0 libpcsclite1 <NOFILES> -0 0 libxaw6 <NOFILES> -0 0 bzr-svn <NOFILES> -0 0 gcc-doc-base <NOFILES> -0 0 libgtk1.2 <NOFILES> -0 0 python2.4-doc <NOFILES> -0 0 liferea-xulrunner <NOFILES> -0 0 liblwres30 <NOFILES> -0 0 libdvdread3 <NOFILES> -0 0 liblualib50 <NOFILES> -0 0 libldap-2.3-0 <NOFILES> -0 0 xmms-oggre <NOFILES> -0 0 libestools1.2 <NOFILES> -0 0 libavcodec0d <NOFILES> -0 0 python-qt3 <NOFILES> -0 0 fb-music-high <NOFILES> -0 0 bicyclerepair <NOFILES> -0 0 libgnome2-ruby <NOFILES> -0 0 industrial-cursor-theme <NOFILES> -0 0 python-pyorbit <NOFILES> -0 0 libgtk2.0-0 <NOFILES> -0 0 timidity-interfaces-extra <NOFILES> -0 0 e2fslibs <NOFILES> -0 0 octave <NOFILES> -0 0 xserver-xorg-video-glint <NOFILES> -0 0 libsndfile1 <NOFILES> -0 0 xbitmaps <NOFILES> -0 0 librexml-ruby <NOFILES> -0 0 libdballef3 <NOFILES> -0 0 grep-dctrl <NOFILES> -0 0 gcc-4.1-doc <NOFILES> -0 0 libpanel-applet2-ruby <NOFILES> -0 0 vim-gui-common <NOFILES> -0 0 liboggflac3 <NOFILES> -0 0 w3c-dtd-xhtml <NOFILES> -0 0 gnuplot-x11 <NOFILES> -0 0 ruby-gnome2 <NOFILES> -0 0 gimp-data <NOFILES> -0 0 libqgis0 <NOFILES> -0 0 xserver-xorg-video-all <NOFILES> -0 0 libgdl-1-0 <NOFILES> -0 0 python-sqlobject <NOFILES> -0 0 openoffice.org-thesaurus-en-us <NOFILES> -0 0 xserver-xorg-video-dummy <NOFILES> -0 0 libapr1 <NOFILES> -0 0 libxvmc1 <NOFILES> -0 0 libcurl3-gnutls <NOFILES> -0 0 libgcc1 <NOFILES> -0 0 kdebase-data <NOFILES> -0 0 xpdf <NOFILES> -0 0 libpng12-0 <NOFILES> -0 0 libgammu1 <NOFILES> -0 0 xkb-data <NOFILES> -0 0 libgnomeui32 <NOFILES> -0 0 r-cran-effects <NOFILES> -0 0 libbonoboui2-0 <NOFILES> -0 0 libcommons-cli-java <NOFILES> -0 0 python-protocols <NOFILES> -0 0 libxevie1 <NOFILES> -0 0 xapian-doc <NOFILES> -0 0 libxt-java <NOFILES> -0 0 libsasl2 <NOFILES> -0 0 gimp-svg <NOFILES> -0 0 python-tk <NOFILES> -0 0 xserver-xorg-video-trident <NOFILES> -0 0 x-ttcidfont-conf <NOFILES> -0 0 r-cran-vr <NOFILES> -0 0 python-magic <NOFILES> -0 0 libgnomeprint2.2-0 <NOFILES> -0 0 r-cran-cluster <NOFILES> -0 0 libavahi-common3 <NOFILES> -0 0 esound-common <NOFILES> -0 0 libgeos2c2a <NOFILES> -0 0 libkpathsea4 <NOFILES> -0 0 libxaw7 <NOFILES> -0 0 xplanet-images <NOFILES> -0 0 libmysqlclient15off <NOFILES> -0 0 libdb3 <NOFILES> -0 0 libfontenc1 <NOFILES> -0 0 xserver-xorg-video-savage <NOFILES> -0 0 libgssapi2 <NOFILES> -0 0 libpstoedit0c2a <NOFILES> -0 0 libgimp2.0 <NOFILES> -0 0 libwrap0 <NOFILES> -0 0 libpt-plugins-v4l <NOFILES> -0 0 glade-doc <NOFILES> -0 0 libxerces2-java <NOFILES> -0 0 libcupsimage2 <NOFILES> -0 0 docbook-xml <NOFILES> -0 0 dict-elements <NOFILES> -0 0 festlex-poslex <NOFILES> -0 0 python-glade2 <NOFILES> -0 0 libcomerr2 <NOFILES> -0 0 liblzo1 <NOFILES> -0 0 liblua50 <NOFILES> -0 0 selinux-policy-refpolicy-targeted <NOFILES> -0 0 libxt6 <NOFILES> -0 0 libboost-program-options1.33.1 <NOFILES> -0 0 libgksu1.2-0 <NOFILES> -0 0 openoffice.org-hyphenation-it <NOFILES> -0 0 python-markdown <NOFILES> -0 0 pgf <NOFILES> -0 0 python-libxml2 <NOFILES> -0 0 libguichan0 <NOFILES> -0 0 libhrit0 <NOFILES> -0 0 libdebian-installer4 <NOFILES> -0 0 libgtkhtml3.8-15 <NOFILES> -0 0 libgnorba27 <NOFILES> -0 0 xserver-xorg-video-v4l <NOFILES> -0 0 linux-sound-base <NOFILES> -0 0 libsoup2.2-8 <NOFILES> -0 0 libreadline5 <NOFILES> -0 0 libsysfs2 <NOFILES> -0 0 libgucharmap4 <NOFILES> -0 0 libsqlite3-0 <NOFILES> -0 0 libbonobo2-0 <NOFILES> -0 0 python-matplotlib-doc <NOFILES> -0 0 libncursesw5 <NOFILES> -0 0 python-gtk2-tutorial <NOFILES> -0 0 python-xapian <NOFILES> -0 0 nautilus-data <NOFILES> -0 0 xserver-xorg-video-neomagic <NOFILES> -0 0 libmikmod2 <NOFILES> -0 0 libmyodbc <NOFILES> -0 0 libxerces27 <NOFILES> -0 0 libecal1.2-6 <NOFILES> -0 0 libopenssl-ruby1.8 <NOFILES> -0 0 libgamin0 <NOFILES> -0 0 python-gnome2-extras <NOFILES> -0 0 python-doc <NOFILES> -0 0 xserver-xorg-input-wacom <NOFILES> -0 0 libgstreamer0.8-ruby <NOFILES> -0 0 bluetooth <NOFILES> -0 0 r-cran-nlme <NOFILES> -0 0 dict-vera <NOFILES> -0 0 libmozjs0d <NOFILES> -0 0 desktop-base <NOFILES> -0 0 libgcrypt11 <NOFILES> -0 0 libgsf-1-114 <NOFILES> -0 0 liblockdev1 <NOFILES> -0 0 libexpat1 <NOFILES> -0 0 mysql-common <NOFILES> -0 0 xserver-xorg-video-sisusb <NOFILES> -0 0 libcap1 <NOFILES> -0 0 libgda2-ruby <NOFILES> -0 0 scim-gtk2-immodule <NOFILES> -0 0 xserver-xorg-video-vesa <NOFILES> -0 0 libxfixes3 <NOFILES> -0 0 libneon26 <NOFILES> -0 0 python-dns <NOFILES> -0 0 mysql-server <NOFILES> -0 0 libg2c0 <NOFILES> -0 0 witalian <NOFILES> -0 0 libavahi-glib1 <NOFILES> -0 0 gtk2-engines-pixbuf <NOFILES> -0 0 libm17n-0 <NOFILES> -0 0 libstlport4.6c2 <NOFILES> -0 0 liblcms1 <NOFILES> -0 0 libedit2 <NOFILES> -0 0 liblog4j1.2-java <NOFILES> -0 0 gnome-icon-theme <NOFILES> -0 0 libxp6 <NOFILES> -0 0 libsamplerate0 <NOFILES> -0 0 libsdl-image1.2 <NOFILES> -0 0 mixxx-data <NOFILES> -0 0 gstreamer0.10-plugins-base <NOFILES> -0 0 libseda-java <NOFILES> -0 0 libxres1 <NOFILES> -0 0 kdemultimedia-kio-plugins <NOFILES> -0 0 libadns1 <NOFILES> -0 0 libgnorbagtk0 <NOFILES> -0 0 fontconfig-config <NOFILES> -0 0 xserver-xorg-video-nv <NOFILES> -0 0 libjack0.100.0-0 <NOFILES> -0 0 firmware-ipw3945 <NOFILES> -0 0 libdns22 <NOFILES> -0 0 gaim-data <NOFILES> -0 0 scim-m17n <NOFILES> -0 0 netcdf-doc <NOFILES> -0 0 libgconf2-ruby <NOFILES> -0 0 libhtree-ruby1.8 <NOFILES> -0 0 libusb-0.1-4 <NOFILES> -0 0 r-cran-sm <NOFILES> -0 0 libmhash2 <NOFILES> -0 0 libaa1 <NOFILES> -0 0 libnspr4-0d <NOFILES> -0 0 libgdbm3 <NOFILES> -0 0 python-configobj <NOFILES> -0 0 libwxbase2.6-0 <NOFILES> -0 0 python-dispatch <NOFILES> -0 0 libautotrace3 <NOFILES> -0 0 libiw28 <NOFILES> -0 0 libpt-plugins-alsa <NOFILES> -0 0 libnautilus-burn3 <NOFILES> -0 0 latex-xcolor <NOFILES> -0 0 xserver-xorg-video-s3virge <NOFILES> -0 0 python-elementtree <NOFILES> -0 0 postgresql-doc-7.4 <NOFILES> -0 0 conglomerate-common <NOFILES> -0 0 libhdf5-doc <NOFILES> -0 0 c++-annotations-contrib <NOFILES> -0 0 libtag1c2a <NOFILES> -0 0 libgtk-mozembed-ruby <NOFILES> -0 0 libxml2 <NOFILES> -0 0 gnome-libs-data <NOFILES> -0 0 paste-common <NOFILES> -0 0 freedroidrpg-data <NOFILES> -0 0 iitalian <NOFILES> -0 0 libltdl3 <NOFILES> -0 0 libavahi-client3 <NOFILES> -0 0 fftw3 <NOFILES> -0 0 libgfortran1 <NOFILES> -0 0 libmyspell3c2 <NOFILES> -0 0 xserver-xorg-video-tga <NOFILES> -0 0 r-cran-car <NOFILES> -0 0 python-newt <NOFILES> -0 0 xserver-xorg-video-ark <NOFILES> -0 0 libesd0 <NOFILES> -0 0 openoffice.org <NOFILES> -0 0 libsdl-net1.2 <NOFILES> -0 0 libsigc++-2.0-0c2a <NOFILES> -0 0 python-simplejson <NOFILES> -0 0 libcdparanoia0 <NOFILES> -0 0 libglib1.2 <NOFILES> -0 0 libpopt0 <NOFILES> -0 0 scribus-ng-doc <NOFILES> -0 0 libwnck-common <NOFILES> -0 0 mplayer-skin-blue <NOFILES> -0 0 libgail-gnome-module <NOFILES> -0 0 libopenexr2c2a <NOFILES> -0 0 libsp1c2 <NOFILES> -0 0 libvdeplug2 <NOFILES> -0 0 libmodplug0c2 <NOFILES> -0 0 ttf-opensymbol <NOFILES> -0 0 libwxgtk2.6-0 <NOFILES> -0 0 libqscintilla6 <NOFILES> -0 0 epiphany-extensions <NOFILES> -0 0 festlex-ifd <NOFILES> -0 0 libgle3 <NOFILES> -0 0 libartsc0 <NOFILES> -0 0 gmt-doc <NOFILES> -0 0 libdb4.2 <NOFILES> -0 0 libcairo-ruby <NOFILES> -0 0 libid3tag0 <NOFILES> -0 0 libogg0 <NOFILES> -0 0 libxdmcp6 <NOFILES> -0 0 python-turbojson <NOFILES> -0 0 gdb-doc <NOFILES> -0 0 python-opengl <NOFILES> -0 0 r-cran-rgl <NOFILES> -0 0 load-dirs-common <NOFILES> -0 0 rrootage-data <NOFILES> -0 0 latex-beamer <NOFILES> -0 0 r-cran-mgcv <NOFILES> -0 0 libfs6 <NOFILES> -0 0 libgtk2.0-common <NOFILES> -0 0 libxapian13 <NOFILES> -0 0 libgnome-pilot2 <NOFILES> -0 0 libx11-data <NOFILES> -0 0 libdballe-bufrex3 <NOFILES> -0 0 liblircclient0 <NOFILES> -0 0 libeel2-2.14 <NOFILES> -0 0 libxxf86dga1 <NOFILES> -0 0 linux-patch-gcov <NOFILES> -0 0 c++-annotations-html <NOFILES> -0 0 libqt4-qt3support <NOFILES> -0 0 libanthy0 <NOFILES> -0 0 python-matplotlib <NOFILES> -0 0 libxi6 <NOFILES> -0 0 docbook-doc <NOFILES> -0 0 liblzo2-2 <NOFILES> -0 0 r-cran-boot <NOFILES> -0 0 libiec61883-0 <NOFILES> -0 0 libisccc30 <NOFILES> -0 0 libtextwrap1 <NOFILES> -0 0 libssp0 <NOFILES> -0 0 libgstreamer0.10-0 <NOFILES> -0 0 gdm-themes <NOFILES> -0 0 balder2d-data <NOFILES> -0 0 libpam0g <NOFILES> -0 0 debian-archive-keyring <NOFILES> -0 0 libgdal1-1.3.2-grass <NOFILES> -0 0 linux-doc-2.6.18 <NOFILES> -0 0 libgnomeprint2-ruby <NOFILES> -0 0 hal-info <NOFILES> -0 0 python-paramiko <NOFILES> -0 0 linux-manual-2.6.21 <NOFILES> -0 0 evolution-common <NOFILES> -0 0 gnome-games-data <NOFILES> -0 0 xserver-xorg-video-i128 <NOFILES> -0 0 libqt3-mt <NOFILES> -0 0 libnids1.21 <NOFILES> -0 0 festlex-cmu <NOFILES> -0 0 gdk-imlib11 <NOFILES> -0 0 python-formencode <NOFILES> -0 0 at-spi <NOFILES> -0 0 libpcap0.8 <NOFILES> -0 0 imlib11 <NOFILES> -0 0 xserver-xorg-input-mouse <NOFILES> -0 0 libxdamage1 <NOFILES> -0 0 bison-doc <NOFILES> -0 0 libgsm1 <NOFILES> -0 0 xmms-osd-plugin <NOFILES> -0 0 compiz <NOFILES> -0 0 debian-cd <NOFILES> -0 0 gimp-texturize <NOFILES> -0 0 ttf-bitstream-vera <NOFILES> -0 0 libxdelta2 <NOFILES> -0 0 libgda2-common <NOFILES> -0 0 libinstrudeo0 <NOFILES> -0 0 xserver-xorg-video-via <NOFILES> -0 0 libxrender1 <NOFILES> -0 0 libavahi-qt3-1 <NOFILES> -0 0 libloudmouth1-0 <NOFILES> -0 0 ttf-freefont <NOFILES> -0 0 c++-annotations-dvi <NOFILES> -0 0 gstreamer0.10-gnomevfs <NOFILES> -0 0 libisccfg1 <NOFILES> -0 0 libuuid1 <NOFILES> -0 0 libcpufreq0 <NOFILES> -0 0 libgcj7-jar <NOFILES> -0 0 xserver-xorg-video-tdfx <NOFILES> -0 0 iceweasel-l10n-en-gb <NOFILES> -0 0 libxext6 <NOFILES> -0 0 libselinux1 <NOFILES> -0 0 libvcdinfo0 <NOFILES> -0 0 ipw3945-source <NOFILES> -0 0 libparted1.7-1 <NOFILES> -0 0 recode-doc <NOFILES> -0 0 libcamel1.2-8 <NOFILES> -0 0 preview-latex-style <NOFILES> -0 0 iso-codes <NOFILES> -0 0 apache2 <NOFILES> -0 0 xfonts-encodings <NOFILES> -0 0 menu-xdg <NOFILES> -0 0 cpp-doc <NOFILES> -0 0 libeel2-data <NOFILES> -0 0 libgtkmm1.2-0c2a <NOFILES> -0 0 libgcj-bc <NOFILES> -0 0 libexif12 <NOFILES> -0 0 libgsmme1c2a <NOFILES> -0 0 gnuplot <NOFILES> -0 0 libglitz1 <NOFILES> -0 0 liblink-grammar4 <NOFILES> -0 0 libxmuu1 <NOFILES> -0 0 libflash0c2 <NOFILES> -0 0 python-pastedeploy <NOFILES> -0 0 libgtk1.2-common <NOFILES> -0 0 libpt-1.10.0 <NOFILES> -0 0 libneon25 <NOFILES> -0 0 gfortran-doc <NOFILES> -0 0 python-tz <NOFILES> -0 0 libatk1.0-0 <NOFILES> -0 0 liborbit0 <NOFILES> -0 0 kamera <NOFILES> -0 0 libxml++2.6-2 <NOFILES> -0 0 libidl0 <NOFILES> -0 0 build-essential <NOFILES> -0 0 libxrandr2 <NOFILES> -0 0 libxslt1.1 <NOFILES> -0 0 libdns32 <NOFILES> -0 0 libgnome-keyring0 <NOFILES> -0 0 libsigc++-1.2-5c2 <NOFILES> -0 0 libguile-ltdl-1 <NOFILES> -0 0 gamin <NOFILES> -0 0 bzrtools <NOFILES> -0 0 python-wxgtk2.6 <NOFILES> -0 0 apache2-doc <NOFILES> -0 0 libnm-glib0 <NOFILES> -0 0 xserver-xorg-video-apm <NOFILES> -0 0 texlive-pstricks <NOFILES> -0 0 libgail17 <NOFILES> -0 0 libk3b2 <NOFILES> -0 0 python-all-dev <NOFILES> -0 0 libksba8 <NOFILES> -0 0 libsdl-ttf2.0-0 <NOFILES> -0 0 libboost-filesystem1.33.1 <NOFILES> -0 0 libpisync0 <NOFILES> -0 0 python2.4-examples <NOFILES> -0 0 libgnomeui-common <NOFILES> -0 0 libexif-ruby <NOFILES> -0 0 libgnomeprintui2.2-0 <NOFILES> -0 0 libstdc++6 <NOFILES> -0 0 gnome-backgrounds <NOFILES> -0 0 libxxf86misc1 <NOFILES> -0 0 libedata-cal1.2-5 <NOFILES> -0 0 libdiscover1 <NOFILES> -0 0 libbind9-0 <NOFILES> -0 0 libquicktime0 <NOFILES> -0 0 totem <NOFILES> -0 0 iceweasel-torbutton <NOFILES> -0 0 r-cran-zoo <NOFILES> -0 0 iamerican <NOFILES> -0 0 libisc32 <NOFILES> -0 0 dict-jargon <NOFILES> -0 0 python-turbokid <NOFILES> -0 0 libpoppler0c2 <NOFILES> -0 0 app-install-data <NOFILES> -0 0 r-cran-survival <NOFILES> -0 0 libhdf5-serial-1.6.5-0 <NOFILES> -0 0 libqt4-sql <NOFILES> -0 0 gstreamer0.10-alsa <NOFILES> -0 0 zlib1g <NOFILES> -0 0 python-mapscript <NOFILES> -0 0 libatspi1.0-0 <NOFILES> -0 0 libcairo2 <NOFILES> -0 0 libungif4g <NOFILES> -0 0 gnome-desktop-environment <NOFILES> -0 0 libgtkhtml2-0 <NOFILES> -0 0 libglibmm-2.4-1c2a <NOFILES> -0 0 libswt-gtk-3.2-jni <NOFILES> -0 0 libgtkglext1 <NOFILES> -0 0 libjpeg62 <NOFILES> -0 0 gtkhtml3.8 <NOFILES> -0 0 xfonts-75dpi <NOFILES> -0 0 python-pyrss2gen <NOFILES> -0 0 r-cran-multcomp <NOFILES> -0 0 php5-mapscript <NOFILES> -0 0 libdballe-core-doc <NOFILES> -0 0 libfontconfig1 <NOFILES> -0 0 php5-gd <NOFILES> -0 0 libidn11 <NOFILES> -0 0 libgdl-1-common <NOFILES> -0 0 libavahi-compat-howl0 <NOFILES> -0 0 xulrunner-gnome-support <NOFILES> -0 0 libgnomeprintui2-ruby <NOFILES> -0 0 cupsys-common <NOFILES> -0 0 libgdk-pixbuf2 <NOFILES> -0 0 libhal-storage1 <NOFILES> -0 0 libisccfg30 <NOFILES> -0 0 libnewt0.52 <NOFILES> -0 0 libchm1 <NOFILES> -0 0 libao2 <NOFILES> -0 0 libacl1 <NOFILES> -0 0 libhdf4g <NOFILES> -0 0 doc-debian <NOFILES> -0 0 libedata-book1.2-2 <NOFILES> -0 0 blt <NOFILES> -0 0 emacsen-common <NOFILES> -0 0 libdbus-qt-1-1c2 <NOFILES> -0 0 libnetpbm10 <NOFILES> -0 0 python-deb822 <NOFILES> -0 0 hicolor-icon-theme <NOFILES> -0 0 libboost-dev <NOFILES> -0 0 libc6-i386 <NOFILES> -0 0 wormux-data <NOFILES> -0 0 libservlet2.4-java <NOFILES> -0 0 libgc1c2 <NOFILES> -0 0 libswt-gtk-3.2-java <NOFILES> -0 0 dict-gcide <NOFILES> -0 0 libdballe-core3 <NOFILES> -0 0 dict-moby-thesaurus <NOFILES> -0 0 libxerces27-dev <NOFILES> -0 0 libavahi-common-data <NOFILES> -0 0 libgtksourceview1-ruby <NOFILES> -0 0 libbz2-1.0 <NOFILES> -0 0 grub-doc <NOFILES> -0 0 xserver-xorg-video-i810 <NOFILES> -0 0 gsfonts <NOFILES> -0 0 libgd2-xpm <NOFILES> -0 0 libgutenprintui2-1 <NOFILES> -0 0 libegroupwise1.2-10 <NOFILES> -0 0 refblas3 <NOFILES> -0 0 autotools-dev <NOFILES> -0 0 libart2 <NOFILES> -0 0 docbook <NOFILES> -0 0 r-cran-rpart <NOFILES> -0 0 texpower-manual <NOFILES> -0 0 python-pexpect <NOFILES> -0 0 liborbit2 <NOFILES> -0 0 librpcsecgss3 <NOFILES> -0 0 libnet6-1.3-0 <NOFILES> -0 0 rubybook <NOFILES> -0 0 python-soappy <NOFILES> -0 0 make-doc <NOFILES> -0 0 libssl0.9.8 <NOFILES> -0 0 libdbus-1-3 <NOFILES> -0 0 adept <NOFILES> -0 0 libgl1-mesa-glx <NOFILES> -0 0 ttf-arphic-uming <NOFILES> -0 0 ceferino-data <NOFILES> -0 0 libglade2-0 <NOFILES> -0 0 texlive-generic-extra <NOFILES> -0 0 libicu36 <NOFILES> -0 0 myspell-it <NOFILES> -0 0 vim-doc <NOFILES> -0 0 libglu1-xorg-dev <NOFILES> -0 0 libtdb1 <NOFILES> -0 0 libarts1-akode <NOFILES> -0 0 xserver-xorg-video-siliconmotion <NOFILES> -0 0 openoffice.org-thesaurus-it <NOFILES> -0 0 capplets-data <NOFILES> -0 0 gsfonts-x11 <NOFILES> -0 0 python-wxversion <NOFILES> -0 0 dballe-common <NOFILES> -0 0 ipw3945-modules-2.6.20.1enrico <NOFILES> -0 0 r-recommended <NOFILES> -0 0 libgrib0 <NOFILES> -0 0 libxxf86vm1 <NOFILES> -0 0 libavc1394-0 <NOFILES> -0 0 gfortran-4.1-doc <NOFILES> -0 0 libgnome-desktop-2 <NOFILES> -0 0 system-tools-backends <NOFILES> -0 0 libpcap-dev <NOFILES> -0 0 mplayer-doc <NOFILES> -0 0 libsqlite0 <NOFILES> -0 0 gutenprint-locales <NOFILES> -0 0 r-cran-lmtest <NOFILES> -0 0 librpm4 <NOFILES> -0 0 libopencdk8 <NOFILES> -0 0 xserver-xorg-video-chips <NOFILES> -0 0 dict-foldoc <NOFILES> -0 0 cgi-mapserver <NOFILES> -0 0 libperl5.8 <NOFILES> -END-POPULARITY-CONTEST-0 TIME:1181000000 diff --git a/ept/test-main.h b/ept/test-main.h deleted file mode 100644 index 7b2afbc..0000000 --- a/ept/test-main.h +++ /dev/null @@ -1,186 +0,0 @@ -// -*- C++ -*- -#include <unistd.h> -#include <sys/wait.h> - -struct Main { - - int suite, test; - int status[2]; - int confirm[2]; - FILE *f_confirm, *f_status; - pid_t pid; - int argc; - char **argv; - pid_t finished; - int status_code; - int test_ok; - - int suite_ok, suite_failed; - int total_ok, total_failed; - - int announced_suite; - std::string current; - - RunAll all; - - Main() : suite(0), test(0) { - suite_ok = suite_failed = 0; - total_ok = total_failed = 0; - test_ok = 0; - announced_suite = -1; - } - - void child() { - close( status[0] ); - close( confirm[1] ); - all.status = fdopen( status[1], "w" ); - all.confirm = fdopen( confirm[0], "r" ); - if ( argc > 1 ) { - RunSuite *s = all.findSuite( argv[1] ); - if (!s) { - std::cerr << "No such suite " << argv[1] << std::endl; - // todo dump possible suites? - exit(250); - } - all.runSuite( *s, test, 0, 1 ); - } - if ( argc == 1 ) { - all.runFrom( suite, test ); - } - fprintf( all.status, "done\n" ); - exit( 0 ); - } - - void testDied() - { - /* std::cerr << "test died: " << test << "/" - << suites[suite].testCount << std::endl; */ - if ( WIFEXITED( status_code ) ) { - if ( WEXITSTATUS( status_code ) == 250 ) - exit( 3 ); - if ( WEXITSTATUS( status_code ) == 0 ) - return; - } - std::cout << "failed test: "<< current; - if ( WIFEXITED( status_code ) ) - std::cout << " (exit status " << WEXITSTATUS( status_code ) << ")"; - if ( WIFSIGNALED( status_code ) ) - std::cout << " (caught signal " << WTERMSIG( status_code ) << ")"; - std::cout << std::endl; - // re-announce the suite - announced_suite --; - ++ test; // continue with next test - test_ok = 0; - suite_failed ++; - } - - void processStatus( const char *line ) { - if ( std::string("done") == line ) { // finished - finished = waitpid( pid, &status_code, 0 ); - assert_eq( pid, finished ); - assert( WIFEXITED( status_code ) ); - assert_eq( WEXITSTATUS( status_code ), 0 ); - std::cout << "overall " << total_ok << "/" - << total_ok + total_failed - << " ok" << std::endl; - exit( total_failed == 0 ? 0 : 1 ); - } - - if ( test_ok ) { - /* std::cerr << "test ok: " << test << "/" - << suites[suite].testCount << std::endl; */ - std::cout << "." << std::flush; - suite_ok ++; - ++ test; - test_ok = 0; - } - - if ( line[0] == 's' ) { - if ( line[2] == 'd' ) { - std::cout << " " << suite_ok << "/" << suite_ok + suite_failed - << " ok" << std::endl; - ++ suite; test = 0; - assert( !test_ok ); - total_ok += suite_ok; - total_failed += suite_failed; - suite_ok = suite_failed = 0; - } - if ( line[2] == 's' ) { - if ( announced_suite < suite ) { - std::cout << line + 5 << ": " << std::flush; - announced_suite = suite; - } - } - } - if ( line[0] == 't' ) { - if ( line[2] == 'd' ) { - fprintf( f_confirm, "ack\n" ); - fflush( f_confirm ); - test_ok = 1; - } - if ( line[2] == 's' ) { - fprintf( f_confirm, "ack\n" ); - fflush( f_confirm ); - current = line + 5; - } - } - } - - void parent() { - close( status[1] ); - close( confirm[0] ); - f_status = fdopen( status[0], "r" ); - f_confirm = fdopen( confirm[1], "w" ); - char *line = 0; - size_t n; - - while ( true ) { - if ( getline( &line, &n, f_status ) < 0 ) { - finished = waitpid( pid, &status_code, 0 ); - if ( finished < 0 ) { - perror( "waitpid failed" ); - exit( 5 ); - } - assert_eq( pid, finished ); - testDied(); - /* std::cerr << "child will be reforked at: " - << suite << " " << test << std::endl; */ - return; - } else { - // std::cerr << "reading pipe: " << line; - line[ strlen( line ) - 1 ] = 0; - processStatus( line ); - free( line ); - } - line = 0; - } - } - - int main( int _argc, char **_argv ) - { - argc = _argc; - argv = _argv; - - all.suiteCount = sizeof(suites)/sizeof(RunSuite); - all.suites = suites; - - while (true) { - if ( pipe( status ) ) - return 1; - if ( pipe( confirm ) ) - return 1; - pid = fork(); - if ( pid < 0 ) - return 2; - if ( pid == 0 ) { // child - child(); - } else { - parent(); - } - } - } -}; - -int main( int argc, char **argv ) { - return Main().main( argc, argv ); -} diff --git a/ept/test-runner.h b/ept/test-runner.h deleted file mode 100644 index 8fe4d12..0000000 --- a/ept/test-runner.h +++ /dev/null @@ -1,63 +0,0 @@ -#include <unistd.h> - -#define RUN(x, y) x().y() - -struct RunTest { - const char *name; - void (*run)(); -}; - -struct RunSuite { - const char *name; - RunTest *tests; - int testCount; -}; - -struct RunAll { - RunSuite *suites; - int suiteCount; - FILE *status, *confirm; - - RunSuite *findSuite( std::string name ) { - for ( int i = 0; i < suiteCount; ++i ) - if ( suites[i].name == name ) - return suites + i; - return 0; - } - - void waitForAck() { - size_t n = 0; char *line = 0; - size_t read = getline( &line, &n, confirm ); - assert_eq( read, 4 ); - assert_eq( std::string( "ack\n" ), line ); - free( line ); - } - - void runSuite( RunSuite &s, int fromTest, int suite, int suiteCount ) - { - fprintf( status, "s/s: (%d/%d) %s\n", suite + 1, suiteCount, s.name ); - for ( int i = fromTest; i < s.testCount; ++i ) { - fprintf( status, "t/s: (%d/%d) %s\n", i, s.testCount, - s.tests[i].name ); - fflush( status ); - waitForAck(); - s.tests[i].run(); - fprintf( status, "t/d: %s\n", s.tests[i].name ); - fflush( status ); - waitForAck(); - // exit( 0 ); // TODO make this optional; safety vs - // performance tradeoff - } - fprintf( status, "s/d: %s\n", s.name ); - } - - void runFrom( int suite, int test ) - { - for ( int i = suite; i < suiteCount; ++i ) { - assert( suite <= suiteCount ); - runSuite( suites[i], test, i, suiteCount ); - test = 0; - } - } -}; - @@ -1,8 +1,8 @@ -//#include <ept/core/apt.h> -#include <ept/config.h> - -#include <wibble/test.h> +#ifndef EPT_TEST_H +#define EPT_TEST_H +#include <ept/utils/tests.h> +#include <ept/config.h> #include <apt-pkg/pkgcache.h> #include <apt-pkg/sourcelist.h> #include <apt-pkg/error.h> @@ -13,29 +13,6 @@ #include <apt-pkg/init.h> #include <cstdlib> - -#ifndef EPT_TEST_H -#define EPT_TEST_H - -struct AptTestEnvironment { - //ept::core::AptDatabase db; - AptTestEnvironment() { - pkgInitConfig (*_config); - _config->Set("Initialized", 1); - _config->Set("Dir", TEST_ENV_DIR); - _config->Set("Dir::Cache", "cache"); - _config->Set("Dir::State", "state"); - _config->Set("Dir::Etc", "etc"); - _config->Set("Dir::Etc::sourcelist", "sources.list"); - _config->Set("Dir::State::status", TEST_ENV_DIR "dpkg-status"); - pkgInitSystem (*_config, _system); - } -}; - -struct DebtagsTestEnvironment : AptTestEnvironment { - DebtagsTestEnvironment() {} -}; - struct EnvOverride { const char* name; diff --git a/ept/token.h b/ept/token.h deleted file mode 100644 index ecacc16..0000000 --- a/ept/token.h +++ /dev/null @@ -1,55 +0,0 @@ -// -*- C++ -*- -#include <wibble/mixin.h> -#include <string> - -#ifndef EPT_TOKEN_H -#define EPT_TOKEN_H - -namespace ept { - -struct Token : wibble::mixin::Comparable< Token > { - std::string _id; // formatted as package[_version] - std::string id() const { return _id; } - - Token() : _id( "" ) {} - Token( std::string s ) : _id( s ) {} - - std::string version() const { - return _id.find( '_' ) == std::string::npos ? "" : - std::string( _id, _id.find( '_' ) + 1, _id.size() ); - } - - std::string package() const { - return std::string( _id, 0, - _id.find( '_' ) == std::string::npos ? - _id.size() : _id.find( '_' ) ); - } - - bool isDesktop() const { - return std::string( _id, 0, 8 ) == "desktop:"; - } - - std::string desktop() const { - return isDesktop() ? std::string( _id, 8, _id.size() ) : ""; - } - - bool hasVersion() const { - return version() != ""; - } - - bool valid() const { - return _id != ""; - } - - bool operator<=( const Token &o ) const { - return _id <= o._id; - } -}; - -} - -inline std::ostream &operator<<( std::ostream &o, const ept::Token &t ) { - return o << t.id(); -} - -#endif diff --git a/ept/utils/string.cc b/ept/utils/string.cc new file mode 100644 index 0000000..95d24fd --- /dev/null +++ b/ept/utils/string.cc @@ -0,0 +1,437 @@ +#include "string.h" +#include <vector> + +using namespace std; + +namespace ept { +namespace str { + +std::string basename(const std::string& pathname) +{ + size_t pos = pathname.rfind("/"); + if (pos == std::string::npos) + return pathname; + else + return pathname.substr(pos+1); +} + +std::string dirname(const std::string& pathname) +{ + if (pathname.empty()) return "."; + + // Skip trailing separators + size_t end = pathname.size(); + while (end > 0 && pathname[end - 1] == '/') + --end; + + // If the result is empty again, then the string was only / characters + if (!end) return "/"; + + // Find the previous separator + end = pathname.rfind("/", end - 1); + + if (end == std::string::npos) + // No previous separator found, everything should be chopped + return std::string("."); + else + { + while (end > 0 && pathname[end - 1] == '/') + --end; + if (!end) return "/"; + return pathname.substr(0, end); + } +} + +void appendpath(std::string& dest, const char* path2) +{ + if (!*path2) + return; + + if (dest.empty()) + { + dest = path2; + return; + } + + if (dest[dest.size() - 1] == '/') + if (path2[0] == '/') + dest += (path2 + 1); + else + dest += path2; + else + if (path2[0] == '/') + dest += path2; + else + { + dest += '/'; + dest += path2; + } +} + +void appendpath(std::string& dest, const std::string& path2) +{ + if (path2.empty()) + return; + + if (dest.empty()) + { + dest = path2; + return; + } + + if (dest[dest.size() - 1] == '/') + if (path2[0] == '/') + dest += path2.substr(1); + else + dest += path2; + else + if (path2[0] == '/') + dest += path2; + else + { + dest += '/'; + dest += path2; + } +} + +std::string joinpath(const std::string& path1, const std::string& path2) +{ + string res = path1; + appendpath(res, path2); + return res; +} + +std::string normpath(const std::string& pathname) +{ + vector<string> st; + if (pathname[0] == '/') + st.push_back("/"); + + Split split(pathname, "/"); + for (const auto& i: split) + { + if (i == "." || i.empty()) continue; + if (i == "..") + if (st.back() == "..") + st.emplace_back(i); + else if (st.back() == "/") + continue; + else + st.pop_back(); + else + st.emplace_back(i); + } + + if (st.empty()) + return "."; + + string res; + for (const auto& i: st) + appendpath(res, i); + return res; +} + +Split::const_iterator::const_iterator(const Split& split) + : split(&split) +{ + // Ignore leading separators if skip_end is true + if (split.skip_empty) skip_separators(); + ++*this; +} + +Split::const_iterator::~const_iterator() +{ +} + +std::string Split::const_iterator::remainder() const +{ + if (end == std::string::npos) + return std::string(); + else + return split->str.substr(end); +}; + +void Split::const_iterator::skip_separators() +{ + const std::string& str = split->str; + const std::string& sep = split->sep; + + while (end + sep.size() <= str.size()) + { + unsigned i = 0; + for ( ; i < sep.size(); ++i) + if (str[end + i] != sep[i]) + break; + if (i < sep.size()) + break; + else + end += sep.size(); + } +} + +Split::const_iterator& Split::const_iterator::operator++() +{ + if (!split) return *this; + + const std::string& str = split->str; + const std::string& sep = split->sep; + bool skip_empty = split->skip_empty; + + /// Convert into an end iterator + if (end == std::string::npos) + { + split = nullptr; + return *this; + } + + /// The string ended with an iterator, and we do not skip empty tokens: + /// return it + if (end == str.size()) + { + cur = string(); + end = std::string::npos; + return *this; + } + + /// Position of the first character past the token that starts at 'end' + size_t tok_end; + if (sep.empty()) + /// If separator is empty, advance one character at a time + tok_end = end + 1; + else + { + /// The token ends at the next separator + tok_end = str.find(sep, end); + } + + /// No more separators found, return from end to the end of the string + if (tok_end == std::string::npos) + { + cur = str.substr(end); + end = std::string::npos; + return *this; + } + + /// We have the boundaries of the current token + cur = str.substr(end, tok_end - end); + + /// Skip the separator + end = tok_end + sep.size(); + + /// Skip all the following separators if skip_empty is true + if (skip_empty) + { + skip_separators(); + if (end == str.size()) + { + end = std::string::npos; + return *this; + } + } + + return *this; +} + +const std::string& Split::const_iterator::operator*() const { return cur; } +const std::string* Split::const_iterator::operator->() const { return &cur; } + +bool Split::const_iterator::operator==(const const_iterator& ti) const +{ + if (!split && !ti.split) return true; + if (split != ti.split) return false; + return end == ti.end; +} + +bool Split::const_iterator::operator!=(const const_iterator& ti) const +{ + if (!split && !ti.split) return false; + if (split != ti.split) return true; + return end != ti.end; +} + + +std::string encode_cstring(const std::string& str) +{ + string res; + for (string::const_iterator i = str.begin(); i != str.end(); ++i) + if (*i == '\n') + res += "\\n"; + else if (*i == '\t') + res += "\\t"; + else if (*i == 0 || iscntrl(*i)) + { + char buf[5]; + snprintf(buf, 5, "\\x%02x", (unsigned int)*i); + res += buf; + } + else if (*i == '"' || *i == '\\') + { + res += "\\"; + res += *i; + } + else + res += *i; + return res; +} + +std::string decode_cstring(const std::string& str, size_t& lenParsed) +{ + string res; + string::const_iterator i = str.begin(); + for ( ; i != str.end() && *i != '"'; ++i) + if (*i == '\\' && (i+1) != str.end()) + { + switch (*(i+1)) + { + case 'n': res += '\n'; break; + case 't': res += '\t'; break; + case 'x': { + size_t j; + char buf[5] = "0x\0\0"; + // Read up to 2 extra hex digits + for (j = 0; j < 2 && i+2+j != str.end() && isxdigit(*(i+2+j)); ++j) + buf[2+j] = *(i+2+j); + i += j; + res += (char)atoi(buf); + break; + } + default: + res += *(i+1); + break; + } + ++i; + } else + res += *i; + if (i != str.end() && *i == '"') + ++i; + lenParsed = i - str.begin(); + return res; +} + +std::string encode_url(const std::string& str) +{ + string res; + for (string::const_iterator i = str.begin(); i != str.end(); ++i) + { + if ( (*i >= '0' && *i <= '9') || (*i >= 'A' && *i <= 'Z') + || (*i >= 'a' && *i <= 'z') || *i == '-' || *i == '_' + || *i == '!' || *i == '*' || *i == '\'' || *i == '(' || *i == ')') + res += *i; + else { + char buf[4]; + snprintf(buf, 4, "%%%02x", static_cast<unsigned>(static_cast<unsigned char>(*i))); + res += buf; + } + } + return res; +} + +std::string decode_url(const std::string& str) +{ + string res; + for (size_t i = 0; i < str.size(); ++i) + { + if (str[i] == '%') + { + // If there's a partial %something at the end, ignore it + if (i >= str.size() - 2) + return res; + res += static_cast<char>(strtoul(str.substr(i+1, 2).c_str(), 0, 16)); + i += 2; + } + else + res += str[i]; + } + return res; +} + +static const char* base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +template<typename T> +static const char invbase64(const T& idx) +{ + static const char data[] = {62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + if (idx < 43) return 0; + if (static_cast<unsigned>(idx) > 43 + (sizeof(data)/sizeof(data[0]))) return 0; + return data[idx - 43]; +} + +std::string encode_base64(const std::string& str) +{ + std::string res; + + for (size_t i = 0; i < str.size(); i += 3) + { + // Pack every triplet into 24 bits + unsigned int enc; + if (i + 3 < str.size()) + enc = ((unsigned char)str[i] << 16) | ((unsigned char)str[i + 1] << 8) | (unsigned char)str[i + 2]; + else + { + enc = ((unsigned char)str[i] << 16); + if (i + 1 < str.size()) + enc |= (unsigned char)str[i + 1] << 8; + if (i + 2 < str.size()) + enc |= (unsigned char)str[i + 2]; + } + + // Divide in 4 6-bit values and use them as indexes in the base64 char + // array + for (int j = 18; j >= 0; j -= 6) + res += base64[(enc >> j) & 63]; + } + + // Replace padding characters with '=' + if (str.size() % 3) + for (size_t i = 0; i < 3 - (str.size() % 3); ++i) + res[res.size() - i - 1] = '='; + + return res; +} + +std::string decode_base64(const std::string& str) +{ + std::string res; + + for (size_t i = 0; i < str.size(); i += 4) + { + // Pack every quadruplet into 24 bits + unsigned int enc; + if (i+4 < str.size()) + { + enc = (invbase64(str[i]) << 18) + + (invbase64(str[i+1]) << 12) + + (invbase64(str[i+2]) << 6) + + (invbase64(str[i+3])); + } else { + enc = (invbase64(str[i]) << 18); + if (i+1 < str.size()) + enc += (invbase64(str[i+1]) << 12); + if (i+2 < str.size()) + enc += (invbase64(str[i+2]) << 6); + if (i+3 < str.size()) + enc += (invbase64(str[i+3])); + } + + // Divide in 3 8-bit values and append them to the result + res += enc >> 16 & 0xff; + res += enc >> 8 & 0xff; + res += enc & 0xff; + } + + // Remove trailing padding + if (str.size() > 0) + for (size_t i = str.size() - 1; str[i] == '='; --i) + { + if (res.size() > 0) + res.resize(res.size() - 1); + if (i == 0 || res.size() == 0 ) + break; + } + + return res; +} + + +} +} diff --git a/ept/utils/string.h b/ept/utils/string.h new file mode 100644 index 0000000..5988365 --- /dev/null +++ b/ept/utils/string.h @@ -0,0 +1,301 @@ +#ifndef EPT_STRING_H +#define EPT_STRING_H + +/** + * @author Enrico Zini <enrico@enricozini.org> + * @brief String functions + * + * Copyright (C) 2007--2015 Enrico Zini <enrico@debian.org> + */ + +#include <string> +#include <functional> +#include <sstream> +#include <cctype> + +namespace ept { +namespace str { + +/// Check if a string starts with the given substring +inline bool startswith(const std::string& str, const std::string& part) +{ + if (str.size() < part.size()) + return false; + return str.substr(0, part.size()) == part; +} + +/// Check if a string ends with the given substring +inline bool endswith(const std::string& str, const std::string& part) +{ + if (str.size() < part.size()) + return false; + return str.substr(str.size() - part.size()) == part; +} + +/** + * Stringify and join a sequence of objects + */ +template<typename ITER> +std::string join(const std::string& sep, const ITER& begin, const ITER& end) +{ + std::stringstream res; + bool first = true; + for (ITER i = begin; i != end; ++i) + { + if (first) + first = false; + else + res << sep; + res << *i; + } + return res.str(); +} + +/** + * Stringify and join an iterable container + */ +template<typename ITEMS> +std::string join(const std::string& sep, const ITEMS& items) +{ + std::stringstream res; + bool first = true; + for (const auto& i: items) + { + if (first) + first = false; + else + res << sep; + res << i; + } + return res.str(); +} + +/** + * Return the substring of 'str' without all leading characters for which + * 'classifier' returns true. + */ +template<typename FUN> +inline std::string lstrip(const std::string& str, const FUN& classifier) +{ + if (str.empty()) + return str; + + size_t beg = 0; + while (beg < str.size() && classifier(str[beg])) + ++beg; + + return str.substr(beg, str.size() - beg + 1); +} + +/** + * Return the substring of 'str' without all leading spaces. + */ +inline std::string lstrip(const std::string& str) +{ + return lstrip(str, ::isspace); +} + +/** + * Return the substring of 'str' without all trailing characters for which + * 'classifier' returns true. + */ +template<typename FUN> +inline std::string rstrip(const std::string& str, const FUN& classifier) +{ + if (str.empty()) + return str; + + size_t end = str.size(); + while (end > 0 && classifier(str[end - 1])) + --end; + + if (end == 0) + return std::string(); + else + return str.substr(0, end); +} + +/** + * Return the substring of 'str' without all trailing spaces. + */ +inline std::string rstrip(const std::string& str) +{ + return rstrip(str, ::isspace); +} + +/** + * Return the substring of 'str' without all leading and trailing characters + * for which 'classifier' returns true. + */ +template<typename FUN> +inline std::string strip(const std::string& str, const FUN& classifier) +{ + if (str.empty()) + return str; + + size_t beg = 0; + size_t end = str.size() - 1; + while (beg < end && classifier(str[beg])) + ++beg; + while (end >= beg && classifier(str[end])) + --end; + + return str.substr(beg, end-beg+1); +} + +/** + * Return the substring of 'str' without all leading and trailing spaces. + */ +inline std::string strip(const std::string& str) +{ + return strip(str, ::isspace); +} + +/// Return an uppercased copy of str +inline std::string upper(const std::string& str) +{ + std::string res; + res.reserve(str.size()); + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) + res += ::toupper(*i); + return res; +} + +/// Return a lowercased copy of str +inline std::string lower(const std::string& str) +{ + std::string res; + res.reserve(str.size()); + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) + res += ::tolower(*i); + return res; +} + +/// Given a pathname, return the file name without its path +std::string basename(const std::string& pathname); + +/// Given a pathname, return the directory name without the file name +std::string dirname(const std::string& pathname); + +/// Append path2 to path1, adding slashes when appropriate +void appendpath(std::string& dest, const char* path2); + +/// Append path2 to path1, adding slashes when appropriate +void appendpath(std::string& dest, const std::string& path2); + +/// Append an arbitrary number of path components to \a dest +template<typename S1, typename S2, typename... Args> +void appendpath(std::string& dest, S1 first, S2 second, Args... next) +{ + appendpath(dest, first); + appendpath(dest, second, next...); +} + +/// Join two or more paths, adding slashes when appropriate +template<typename... Args> +std::string joinpath(Args... components) +{ + std::string res; + appendpath(res, components...); + return res; +} + +/** + * Normalise a pathname. + * + * For example, A//B, A/./B and A/foo/../B all become A/B. + */ +std::string normpath(const std::string& pathname); + +/** + * Split a string where a given substring is found + * + * This does a similar work to the split functions of perl, python and ruby. + * + * Example code: + * \code + * str::Split splitter(my_string, "/"); + * vector<string> split; + * std::copy(splitter.begin(), splitter.end(), back_inserter(split)); + * \endcode + */ +struct Split +{ + /// String to split + std::string str; + /// Separator + std::string sep; + /** + * If true, skip empty tokens, effectively grouping consecutive separators + * as if they were a single one + */ + bool skip_empty; + + Split(const std::string& str, const std::string& sep, bool skip_empty=false) + : str(str), sep(sep), skip_empty(skip_empty) {} + + class const_iterator : public std::iterator<std::input_iterator_tag, std::string> + { + protected: + const Split* split = nullptr; + /// Current token + std::string cur; + /// Position of the first character of the next token + size_t end = 0; + + /// Move end past all the consecutive separators that start at its position + void skip_separators(); + + public: + /// Begin iterator + const_iterator(const Split& split); + /// End iterator + const_iterator() {} + ~const_iterator(); + + const_iterator& operator++(); + const std::string& operator*() const; + const std::string* operator->() const; + + std::string remainder() const; + + bool operator==(const const_iterator& ti) const; + bool operator!=(const const_iterator& ti) const; + }; + + /// Return the begin iterator to split a string on instances of sep + const_iterator begin() { return const_iterator(*this); } + + /// Return the end iterator to string split + const_iterator end() { return const_iterator(); } +}; + +/** + * Escape the string so it can safely used as a C string inside double quotes + */ +std::string encode_cstring(const std::string& str); + +/** + * Unescape a C string, stopping at the first double quotes or at the end of + * the string. + * + * lenParsed is set to the number of characters that were pased (which can be + * greather than the size of the resulting string in case escapes were found) + */ +std::string decode_cstring(const std::string& str, size_t& lenParsed); + +/// Urlencode a string +std::string encode_url(const std::string& str); + +/// Decode an urlencoded string +std::string decode_url(const std::string& str); + +/// Encode a string in Base64 +std::string encode_base64(const std::string& str); + +/// Decode a string encoded in Base64 +std::string decode_base64(const std::string& str); + +} +} +#endif diff --git a/ept/utils/sys.cc b/ept/utils/sys.cc new file mode 100644 index 0000000..8f6f2ff --- /dev/null +++ b/ept/utils/sys.cc @@ -0,0 +1,786 @@ +#include "sys.h" +#include "string.h" +#include <cstddef> +#include <cstring> +#include <exception> +#include <sstream> +#include <system_error> +#include <cerrno> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <alloca.h> + +namespace { + +inline const char* to_cstring(const std::string& s) +{ + return s.c_str(); +} + +inline const char* to_cstring(const char* s) +{ + return s; +} + +} + +namespace ept { +namespace sys { + +std::unique_ptr<struct stat> stat(const std::string& pathname) +{ + std::unique_ptr<struct stat> res(new struct stat); + if (::stat(pathname.c_str(), res.get()) == -1) + { + if (errno == ENOENT) + return std::unique_ptr<struct stat>(); + else + throw std::system_error(errno, std::system_category(), "cannot stat " + pathname); + } + return res; +} + +void stat(const std::string& pathname, struct stat& st) +{ + if (::stat(pathname.c_str(), &st) == -1) + throw std::system_error(errno, std::system_category(), "cannot stat " + pathname); +} + +#define common_stat_body(testfunc) \ + struct stat st; \ + if (::stat(pathname.c_str(), &st) == -1) { \ + if (errno == ENOENT) \ + return false; \ + else \ + throw std::system_error(errno, std::system_category(), "cannot stat " + pathname); \ + } \ + return testfunc(st.st_mode) + +bool isdir(const std::string& pathname) +{ + common_stat_body(S_ISDIR); +} + +bool isblk(const std::string& pathname) +{ + common_stat_body(S_ISBLK); +} + +bool ischr(const std::string& pathname) +{ + common_stat_body(S_ISCHR); +} + +bool isfifo(const std::string& pathname) +{ + common_stat_body(S_ISFIFO); +} + +bool islnk(const std::string& pathname) +{ + common_stat_body(S_ISLNK); +} + +bool isreg(const std::string& pathname) +{ + common_stat_body(S_ISREG); +} + +bool issock(const std::string& pathname) +{ + common_stat_body(S_ISSOCK); +} + +#undef common_stat_body + +time_t timestamp(const std::string& file) +{ + struct stat st; + stat(file, st); + return st.st_mtime; +} + +time_t timestamp(const std::string& file, time_t def) +{ + auto st = sys::stat(file); + return st.get() ? st->st_mtime : def; +} + +size_t size(const std::string& file) +{ + struct stat st; + stat(file, st); + return (size_t)st.st_size; +} + +size_t size(const std::string& file, size_t def) +{ + auto st = sys::stat(file); + return st.get() ? (size_t)st->st_size : def; +} + +ino_t inode(const std::string& file) +{ + struct stat st; + stat(file, st); + return st.st_ino; +} + +ino_t inode(const std::string& file, ino_t def) +{ + auto st = sys::stat(file); + return st.get() ? st->st_ino : def; +} + + +bool access(const std::string &s, int m) +{ + return ::access(s.c_str(), m) == 0; +} + +bool exists(const std::string& file) +{ + return sys::access(file, F_OK); +} + +std::string getcwd() +{ +#if defined(__GLIBC__) + char* cwd = ::get_current_dir_name(); + if (cwd == NULL) + throw std::system_error(errno, std::system_category(), "cannot get the current working directory"); + const std::string str(cwd); + ::free(cwd); + return str; +#else + size_t size = pathconf(".", _PC_PATH_MAX); + char *buf = (char *)alloca( size ); + if (::getcwd(buf, size) == NULL) + throw std::system_error(errno, std::system_category(), "cannot get the current working directory"); + return buf; +#endif +} + +std::string abspath(const std::string& pathname) +{ + if (pathname[0] == '/') + return str::normpath(pathname); + else + return str::normpath(str::joinpath(sys::getcwd(), pathname)); +} + + +/* + * MMap + */ + +MMap::MMap(void* addr, size_t length) + : addr(addr), length(length) +{ +} + +MMap::MMap(MMap&& o) + : addr(o.addr), length(o.length) +{ + o.addr = MAP_FAILED; + o.length = 0; +} + +MMap& MMap::operator=(MMap&& o) +{ + if (this == &o) return *this; + + munmap(); + addr = o.addr; + length = o.length; + o.addr = MAP_FAILED; + o.length = 0; + return *this; +} + +MMap::~MMap() +{ + if (addr != MAP_FAILED) ::munmap(addr, length); +} + +void MMap::munmap() +{ + if (::munmap(addr, length) == -1) + throw std::system_error(errno, std::system_category(), "cannot unmap memory"); + addr = MAP_FAILED; +} + + +/* + * FileDescriptor + */ + +FileDescriptor::FileDescriptor() {} +FileDescriptor::FileDescriptor(FileDescriptor&& o) + : fd(o.fd) +{ + o.fd = -1; +} +FileDescriptor::FileDescriptor(int fd) : fd(fd) {} +FileDescriptor::~FileDescriptor() {} + +void FileDescriptor::throw_error(const char* desc) +{ + throw std::system_error(errno, std::system_category(), desc); +} + +void FileDescriptor::close() +{ + if (fd == -1) return; + if (::close(fd) == -1) + throw_error("cannot close"); + fd = -1; +} + +void FileDescriptor::fstat(struct stat& st) +{ + if (::fstat(fd, &st) == -1) + throw_error("cannot stat"); +} + +void FileDescriptor::fchmod(mode_t mode) +{ + if (::fchmod(fd, mode) == -1) + throw_error("cannot fchmod"); +} + +size_t FileDescriptor::write(const void* buf, size_t count) +{ + ssize_t res = ::write(fd, buf, count); + if (res == -1) + throw_error("cannot write"); + return res; +} + +void FileDescriptor::write_all(const void* buf, size_t count) +{ + size_t written = 0; + while (written < count) + written += write((unsigned char*)buf + written, count - written); +} + +MMap FileDescriptor::mmap(size_t length, int prot, int flags, off_t offset) +{ + void* res =::mmap(0, length, prot, flags, fd, offset); + if (res == MAP_FAILED) + throw_error("cannot mmap"); + return MMap(res, length); +} + + +/* + * NamedFileDescriptor + */ + +NamedFileDescriptor::NamedFileDescriptor(int fd, const std::string& pathname) + : FileDescriptor(fd), pathname(pathname) +{ +} + +NamedFileDescriptor::NamedFileDescriptor(NamedFileDescriptor&& o) + : FileDescriptor(std::move(o)), pathname(std::move(o.pathname)) +{ +} + +NamedFileDescriptor& NamedFileDescriptor::operator=(NamedFileDescriptor&& o) +{ + if (this == &o) return *this; + fd = o.fd; + pathname = std::move(o.pathname); + o.fd = -1; + return *this; +} + +void NamedFileDescriptor::throw_error(const char* desc) +{ + throw std::system_error(errno, std::system_category(), pathname + ": " + desc); +} + + +/* + * Path + */ + +Path::Path(const char* pathname, int flags) + : NamedFileDescriptor(-1, pathname) +{ + fd = open(pathname, flags | O_PATH); + if (fd == -1) + throw_error("cannot open path"); +} + +Path::Path(const std::string& pathname, int flags) + : NamedFileDescriptor(-1, pathname) +{ + fd = open(pathname.c_str(), flags | O_PATH); + if (fd == -1) + throw_error("cannot open path"); +} + +Path::Path(Path& parent, const char* pathname, int flags) + : NamedFileDescriptor(parent.openat(pathname, flags | O_PATH), + str::joinpath(parent.name(), pathname)) +{ +} + +Path::~Path() +{ + if (fd != -1) + ::close(fd); +} + +DIR* Path::fdopendir() +{ + int fd1 = ::openat(fd, ".", O_DIRECTORY); + if (fd1 == -1) + throw_error("cannot open directory"); + + DIR* res = ::fdopendir(fd1); + if (!res) + throw_error("cannot fdopendir"); + + return res; +} + +Path::iterator Path::begin() +{ + if (fd == -1) + return iterator(); + else + return iterator(*this); +} + +Path::iterator Path::end() +{ + return iterator(); +} + +int Path::openat(const char* pathname, int flags, mode_t mode) +{ + int res = ::openat(fd, pathname, flags, mode); + if (res == -1) + throw_error("cannot openat"); + return res; +} + +void Path::fstatat(const char* pathname, struct stat& st) +{ + if (::fstatat(fd, pathname, &st, 0) == -1) + throw_error("cannot fstatat"); +} + +void Path::lstatat(const char* pathname, struct stat& st) +{ + if (::fstatat(fd, pathname, &st, AT_SYMLINK_NOFOLLOW) == -1) + throw_error("cannot fstatat"); +} + +void Path::unlinkat(const char* pathname) +{ + if (::unlinkat(fd, pathname, 0) == -1) + throw_error("cannot unlinkat"); +} + +void Path::rmdirat(const char* pathname) +{ + if (::unlinkat(fd, pathname, AT_REMOVEDIR) == -1) + throw_error("cannot unlinkat"); +} + +Path::iterator::iterator() +{ +} + +Path::iterator::iterator(Path& dir) + : path(&dir) +{ + this->dir = dir.fdopendir(); + + long name_max = fpathconf(dir.fd, _PC_NAME_MAX); + if (name_max == -1) // Limit not defined, or error: take a guess + name_max = 255; + size_t len = offsetof(dirent, d_name) + name_max + 1; + cur_entry = (struct dirent*)malloc(len); + if (cur_entry == NULL) + throw std::bad_alloc(); + + operator++(); +} + +Path::iterator::~iterator() +{ + if (cur_entry) free(cur_entry); + if (dir) closedir(dir); +} + +bool Path::iterator::operator==(const iterator& i) const +{ + if (!dir && !i.dir) return true; + if (!dir || !i.dir) return false; + return cur_entry->d_ino == i.cur_entry->d_ino; +} +bool Path::iterator::operator!=(const iterator& i) const +{ + if (!dir && !i.dir) return false; + if (!dir || !i.dir) return true; + return cur_entry->d_ino != i.cur_entry->d_ino; +} + +void Path::iterator::operator++() +{ + struct dirent* result; + if (readdir_r(dir, cur_entry, &result) != 0) + path->throw_error("cannot readdir_r"); + + if (result == nullptr) + { + // Turn into an end iterator + free(cur_entry); + cur_entry = nullptr; + closedir(dir); + dir = nullptr; + } +} + +bool Path::iterator::isdir() const +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(HAVE_STRUCT_DIRENT_D_TYPE) + if (cur_entry->d_type == DT_DIR) + return true; + if (cur_entry->d_type != DT_UNKNOWN) + return false; +#endif + // No d_type, we'll need to stat + struct stat st; + path->fstatat(cur_entry->d_name, st); + return S_ISDIR(st.st_mode); +} + +bool Path::iterator::isblk() const +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(HAVE_STRUCT_DIRENT_D_TYPE) + if (cur_entry->d_type == DT_BLK) + return true; + if (cur_entry->d_type != DT_UNKNOWN) + return false; +#endif + // No d_type, we'll need to stat + struct stat st; + path->fstatat(cur_entry->d_name, st); + return S_ISBLK(st.st_mode); +} + +bool Path::iterator::ischr() const +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(HAVE_STRUCT_DIRENT_D_TYPE) + if (cur_entry->d_type == DT_CHR) + return true; + if (cur_entry->d_type != DT_UNKNOWN) + return false; +#endif + // No d_type, we'll need to stat + struct stat st; + path->fstatat(cur_entry->d_name, st); + return S_ISCHR(st.st_mode); +} + +bool Path::iterator::isfifo() const +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(HAVE_STRUCT_DIRENT_D_TYPE) + if (cur_entry->d_type == DT_FIFO) + return true; + if (cur_entry->d_type != DT_UNKNOWN) + return false; +#endif + // No d_type, we'll need to stat + struct stat st; + path->fstatat(cur_entry->d_name, st); + return S_ISFIFO(st.st_mode); +} + +bool Path::iterator::islnk() const +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(HAVE_STRUCT_DIRENT_D_TYPE) + if (cur_entry->d_type == DT_LNK) + return true; + if (cur_entry->d_type != DT_UNKNOWN) + return false; +#endif + struct stat st; + path->fstatat(cur_entry->d_name, st); + return S_ISLNK(st.st_mode); +} + +bool Path::iterator::isreg() const +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(HAVE_STRUCT_DIRENT_D_TYPE) + if (cur_entry->d_type == DT_REG) + return true; + if (cur_entry->d_type != DT_UNKNOWN) + return false; +#endif + struct stat st; + path->fstatat(cur_entry->d_name, st); + return S_ISREG(st.st_mode); +} + +bool Path::iterator::issock() const +{ +#if defined(_DIRENT_HAVE_D_TYPE) || defined(HAVE_STRUCT_DIRENT_D_TYPE) + if (cur_entry->d_type == DT_SOCK) + return true; + if (cur_entry->d_type != DT_UNKNOWN) + return false; +#endif + struct stat st; + path->fstatat(cur_entry->d_name, st); + return S_ISSOCK(st.st_mode); +} + + +void Path::rmtree() +{ + for (auto i = begin(); i != end(); ++i) + { + if (strcmp(i->d_name, ".") == 0 || strcmp(i->d_name, "..") == 0) continue; + if (i.isdir()) + { + Path sub(*this, i->d_name); + sub.rmtree(); + } + else + unlinkat(i->d_name); + } + // TODO: is there a way to do this using fd instead? + rmdir(name()); +} + +/* + * File + */ + +File::File(const std::string& pathname, int flags, mode_t mode) + : NamedFileDescriptor(-1, pathname) +{ + fd = open(pathname.c_str(), flags, mode); + if (fd == -1) + throw std::system_error(errno, std::system_category(), "cannot open file " + pathname); +} + +File::~File() +{ + if (fd != -1) ::close(fd); +} + +File File::mkstemp(const std::string& prefix) +{ + char* fbuf = (char*)alloca(prefix.size() + 7); + memcpy(fbuf, prefix.data(), prefix.size()); + memcpy(fbuf + prefix.size(), "XXXXXX", 7); + int fd = ::mkstemp(fbuf); + if (fd < 0) + throw std::system_error(errno, std::system_category(), std::string("cannot create temporary file ") + fbuf); + return File(fd, fbuf); +} + +std::string read_file(const std::string& file) +{ + File in(file, O_RDONLY); + + // Get the file size + struct stat st; + in.fstat(st); + + // mmap the input file + MMap src = in.mmap(st.st_size, PROT_READ, MAP_SHARED); + + return std::string((const char*)src, st.st_size); +} + +void write_file(const std::string& file, const std::string& data, mode_t mode) +{ + File out(file, O_WRONLY | O_CREAT, mode); + out.write_all(data.data(), data.size()); + out.close(); +} + +void write_file_atomically(const std::string& file, const std::string& data, mode_t mode) +{ + File out = File::mkstemp(file); + + // Read the umask + mode_t mask = umask(0777); + umask(mask); + + // Set the file permissions, honoring umask + out.fchmod(mode & ~mask); + + out.write_all(data.data(), data.size()); + out.close(); + + if (rename(out.name().c_str(), file.c_str()) < 0) + throw std::system_error(errno, std::system_category(), "cannot rename " + out.name() + " to " + file); +} + +#if 0 +void mkFilePath(const std::string& file) +{ + size_t pos = file.rfind('/'); + if (pos != std::string::npos) + mkpath(file.substr(0, pos)); +} +#endif + +bool unlink_ifexists(const std::string& file) +{ + if (::unlink(file.c_str()) != 0) + { + if (errno != ENOENT) + throw std::system_error(errno, std::system_category(), "cannot unlink " + file); + else + return false; + } + else + return true; +} + +bool rename_ifexists(const std::string& src, const std::string& dst) +{ + if (::rename(src.c_str(), dst.c_str()) != 0) + { + if (errno != ENOENT) + throw std::system_error(errno, std::system_category(), "cannot rename " + src + " to " + dst); + else + return false; + } + else + return true; +} + +template<typename String> +static void impl_mkdir_ifmissing(String pathname, mode_t mode) +{ + for (unsigned i = 0; i < 5; ++i) + { + // If it does not exist, make it + if (::mkdir(to_cstring(pathname), mode) != -1) + return; + + // throw on all errors except EEXIST. Note that EEXIST "includes the case + // where pathname is a symbolic link, dangling or not." + if (errno != EEXIST && errno != EISDIR) + { + std::stringstream msg; + msg << "cannot create directory " << pathname; + throw std::system_error(errno, std::system_category(), msg.str()); + } + + // Ensure that, if dir exists, it is a directory + std::unique_ptr<struct stat> st = sys::stat(pathname); + if (st.get() == NULL) + { + // Either dir has just been deleted, or we hit a dangling + // symlink. + // + // Retry creating a directory: the more we keep failing, the more + // the likelyhood of a dangling symlink increases. + // + // We could lstat here, but it would add yet another case for a + // race condition if the broken symlink gets deleted between the + // stat and the lstat. + continue; + } + else if (!S_ISDIR(st->st_mode)) + { + // If it exists but it is not a directory, complain + std::stringstream msg; + msg << pathname << " exists but is not a directory"; + throw std::runtime_error(msg.str()); + } + else + // If it exists and it is a directory, we're fine + return; + } + std::stringstream msg; + msg << pathname << " exists and looks like a dangling symlink"; + throw std::runtime_error(msg.str()); +} + +void mkdir_ifmissing(const char* pathname, mode_t mode) +{ + return impl_mkdir_ifmissing(pathname, mode); +} + +void mkdir_ifmissing(const std::string& pathname, mode_t mode) +{ + return impl_mkdir_ifmissing(pathname, mode); +} + +void makedirs(const std::string& pathname, mode_t mode) +{ + if (pathname == "/" || pathname == ".") return; + std::string parent = str::dirname(pathname); + + // First ensure that the upper path exists + makedirs(parent, mode); + + // Then create this dir + mkdir_ifmissing(pathname, mode); +} + +std::string which(const std::string& name) +{ + // argv[0] has an explicit path: ensure it becomes absolute + if (name.find('/') != std::string::npos) + return sys::abspath(name); + + // argv[0] has no explicit path, look for it in $PATH + const char* path = getenv("PATH"); + if (!path) return name; + + str::Split splitter(path, ":", true); + for (const auto& i: splitter) + { + std::string candidate = str::joinpath(i, name); + if (sys::access(candidate, X_OK)) + return sys::abspath(candidate); + } + + return name; +} + +void unlink(const std::string& pathname) +{ + if (::unlink(pathname.c_str()) < 0) + throw std::system_error(errno, std::system_category(), "cannot unlink " + pathname); +} + +void rmdir(const std::string& pathname) +{ + if (::rmdir(pathname.c_str()) < 0) + throw std::system_error(errno, std::system_category(), "cannot rmdir " + pathname); +} + +void rmtree(const std::string& pathname) +{ + Path path(pathname); + path.rmtree(); +} + +#if 0 +std::string mkdtemp( std::string tmpl ) +{ + char *_tmpl = reinterpret_cast< char * >( alloca( tmpl.size() + 1 ) ); + strcpy( _tmpl, tmpl.c_str() ); + return ::mkdtemp( _tmpl ); +} +#endif +} +} diff --git a/ept/utils/sys.h b/ept/utils/sys.h new file mode 100644 index 0000000..334c983 --- /dev/null +++ b/ept/utils/sys.h @@ -0,0 +1,468 @@ +#ifndef EPT_SYS_H +#define EPT_SYS_H + +/** + * @author Enrico Zini <enrico@enricozini.org> + * @brief Operating system functions + * + * Copyright (C) 2007--2015 Enrico Zini <enrico@debian.org> + */ + +#include <string> +//#include <iosfwd> +#include <memory> +#include <iterator> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +namespace ept { +namespace sys { + +/** + * stat() the given file and return the struct stat with the results. + * If the file does not exist, return NULL. + * Raises exceptions in case of errors. + */ +std::unique_ptr<struct stat> stat(const std::string& pathname); + +/** + * stat() the given file filling in the given structure. + * Raises exceptions in case of errors, including if the file does not exist. + */ +void stat(const std::string& pathname, struct stat& st); + +/** + * Returns true if the given pathname is a directory, else false. + * + * It also returns false if the pathname does not exist. + */ +bool isdir(const std::string& pathname); + +/// Same as isdir but checks for block devices +bool isblk(const std::string& pathname); + +/// Same as isdir but checks for character devices +bool ischr(const std::string& pathname); + +/// Same as isdir but checks for FIFOs +bool isfifo(const std::string& pathname); + +/// Same as isdir but checks for symbolic links +bool islnk(const std::string& pathname); + +/// Same as isdir but checks for regular files +bool isreg(const std::string& pathname); + +/// Same as isdir but checks for sockets +bool issock(const std::string& pathname); + +/// File mtime +time_t timestamp(const std::string& file); + +/// File mtime (or def if the file does not exist) +time_t timestamp(const std::string& file, time_t def); + +/// File size +size_t size(const std::string& file); + +/// File size (or def if the file does not exist) +size_t size(const std::string& file, size_t def); + +/// File inode number +ino_t inode(const std::string& file); + +/// File inode number (or 0 if the file does not exist) +ino_t inode(const std::string& file, ino_t def); + +/// access() a filename +bool access(const std::string& s, int m); + +/// Same as access(s, F_OK); +bool exists(const std::string& s); + +/// Get the absolute path of the current working directory +std::string getcwd(); + +/// Get the absolute path of a file +std::string abspath(const std::string& pathname); + +/** + * Wraps a mmapped memory area, unmapping it on destruction. + * + * MMap objects can be used as normal pointers + */ +class MMap +{ + void* addr; + size_t length; + +public: + MMap(const MMap&) = delete; + MMap(MMap&&); + MMap(void* addr, size_t length); + ~MMap(); + + MMap& operator=(const MMap&) = delete; + MMap& operator=(MMap&&); + + size_t size() const { return length; } + + void munmap(); + + template<typename T> + operator const T*() const { return reinterpret_cast<const T*>(addr); } + + template<typename T> + operator T*() const { return reinterpret_cast<T*>(addr); }; +}; + +/** + * Common operations on file descriptors. + * + * Except when documented otherwise, methods of this class are just thin + * wrappers around the libc functions with the same name, that check error + * results and throw exceptions if the functions failed. + * + * Implementing what to do on construction and destruction is left to the + * subclassers: at the FileDescriptor level, the destructor does nothing and + * leaves the file descriptor open. + */ +class FileDescriptor +{ +protected: + int fd = -1; + +public: + FileDescriptor(); + FileDescriptor(FileDescriptor&& o); + FileDescriptor(int fd); + virtual ~FileDescriptor(); + + /** + * Throw an exception based on errno and the given message. + * + * This can be overridden by subclasses that may have more information + * about the file descriptor, so that they can generate more descriptive + * messages. + */ + [[noreturn]] virtual void throw_error(const char* desc); + + void close(); + + void fstat(struct stat& st); + void fchmod(mode_t mode); + + size_t write(const void* buf, size_t count); + + /** + * Write all the data in buf, retrying partial writes + */ + void write_all(const void* buf, size_t count); + + MMap mmap(size_t length, int prot, int flags, off_t offset=0); + + operator int() const { return fd; } +}; + + +/** + * File descriptor with a name + */ + +class NamedFileDescriptor : public FileDescriptor +{ +protected: + std::string pathname; + +public: + NamedFileDescriptor(int fd, const std::string& pathname); + NamedFileDescriptor(NamedFileDescriptor&&); + + NamedFileDescriptor& operator=(NamedFileDescriptor&&); + + [[noreturn]] virtual void throw_error(const char* desc); + + /// Return the file pathname + const std::string& name() const { return pathname; } +}; + +/** + * Wrap a path on the file system opened with O_PATH + */ +struct Path : public NamedFileDescriptor +{ + /** + * Iterator for directory entries + */ + struct iterator : public std::iterator<std::input_iterator_tag, struct dirent> + { + Path* path = nullptr; + DIR* dir = nullptr; + struct dirent* cur_entry = nullptr; + + // End iterator + iterator(); + // Start iteration on dir + iterator(Path& dir); + iterator(iterator&) = delete; + iterator(iterator&& o) + : dir(o.dir), cur_entry(o.cur_entry) + { + o.dir = nullptr; + o.cur_entry = nullptr; + } + ~iterator(); + iterator& operator=(iterator&) = delete; + iterator& operator=(iterator&&) = delete; + + bool operator==(const iterator& i) const; + bool operator!=(const iterator& i) const; + struct dirent& operator*() const { return *cur_entry; } + struct dirent* operator->() const { return cur_entry; } + void operator++(); + + /// @return true if we refer to a directory, else false + bool isdir() const; + + /// @return true if we refer to a block device, else false + bool isblk() const; + + /// @return true if we refer to a character device, else false + bool ischr() const; + + /// @return true if we refer to a named pipe (FIFO). + bool isfifo() const; + + /// @return true if we refer to a symbolic link. + bool islnk() const; + + /// @return true if we refer to a regular file. + bool isreg() const; + + /// @return true if we refer to a Unix domain socket. + bool issock() const; + }; + + using NamedFileDescriptor::NamedFileDescriptor; + + /** + * Open the given pathname with flags | O_PATH. + */ + Path(const char* pathname, int flags=0); + /** + * Open the given pathname with flags | O_PATH. + */ + Path(const std::string& pathname, int flags=0); + /** + * Open the given pathname calling parent.openat, with flags | O_PATH + */ + Path(Path& parent, const char* pathname, int flags=0); + Path(const Path&) = delete; + Path(Path&&) = default; + Path& operator=(const Path&) = delete; + Path& operator=(Path&&) = default; + + /** + * The destructor closes the file descriptor, but does not check errors on + * ::close(). + * + * In normal program flow, it is a good idea to explicitly call + * Path::close() in places where it can throw safely. + */ + ~Path(); + + DIR* fdopendir(); + + /// Begin iterator on all directory entries + iterator begin(); + + /// End iterator on all directory entries + iterator end(); + + int openat(const char* pathname, int flags, mode_t mode=0777); + + void fstatat(const char* pathname, struct stat& st); + + /// fstatat with the AT_SYMLINK_NOFOLLOW flag set + void lstatat(const char* pathname, struct stat& st); + + void unlinkat(const char* pathname); + + /// unlinkat with the AT_REMOVEDIR flag set + void rmdirat(const char* pathname); + + /** + * Delete the directory pointed to by this Path, with all its contents. + * + * The path must point to a directory. + */ + void rmtree(); +}; + + +/** + * open(2) file descriptors + */ +class File : public NamedFileDescriptor +{ +public: + using NamedFileDescriptor::NamedFileDescriptor; + + File(File&&) = default; + File(const File&) = delete; + + /// Wrapper around open(2) + File(const std::string& pathname, int flags, mode_t mode=0777); + + /** + * The destructor closes the file descriptor, but does not check errors on + * ::close(). + * + * In normal program flow, it is a good idea to explicitly call + * File::close() in places where it can throw safely. + */ + ~File(); + + File& operator=(const File&) = delete; + File& operator=(File&&) = default; + + static File mkstemp(const std::string& prefix); +}; + +/// Read whole file into memory. Throws exceptions on failure. +std::string read_file(const std::string &file); + +/** + * Write \a data to \a file, replacing existing contents if it already exists. + * + * New files are created with the given permission mode, honoring umask. + * Permissions of existing files do not change. + */ +void write_file(const std::string& file, const std::string& data, mode_t mode=0777); + +/** + * Write \a data to \a file, replacing existing contents if it already exists. + * + * Files are created with the given permission mode, honoring umask. If the + * file already exists, its mode is ignored. + * + * Data is written to a temporary file, then moved to its final destination, to + * ensure an atomic operation. + */ +void write_file_atomically(const std::string& file, const std::string& data, mode_t mode=0777); + +#if 0 +// Create a temporary directory based on a template. +std::string mkdtemp(std::string templ); + +/// Ensure that the path to the given file exists, creating it if it does not. +/// The file itself will not get created. +void mkFilePath(const std::string& file); +#endif + +/** + * Delete a file if it exists. If it does not exist, do nothing. + * + * @return true if the file was deleted, false if it did not exist + */ +bool unlink_ifexists(const std::string& file); + +/** + * Move \a src to \a dst, without raising exception if \a src does not exist + * + * @return true if the file was renamed, false if it did not exist + */ +bool rename_ifexists(const std::string& src, const std::string& dst); + +/// Create the given directory, if it does not already exists. +/// It will complain if the given pathname already exists but is not a +/// directory. +void mkdir_ifmissing(const char* pathname, mode_t mode=0777); + +void mkdir_ifmissing(const std::string& pathname, mode_t mode=0777); + +/// Create all the component of the given directory, including the directory +/// itself. +void makedirs(const std::string& pathname, mode_t=0777); + +/** + * Compute the absolute path of an executable. + * + * If \a name is specified as a partial path, it ensures it is made absolute. + * If \a name is not specified as a path, it looks for the executable in $PATH + * and return its absolute pathname. + */ +std::string which(const std::string& name); + +/// Delete the file using unlink() +void unlink(const std::string& pathname); + +/// Remove the directory using rmdir(2) +void rmdir(const std::string& pathname); + +/// Delete the directory \a pathname and all its contents. +void rmtree(const std::string& pathname); + +#if 0 +/// Nicely wrap access to directories +class Directory +{ +protected: + /// Directory pathname + std::string m_path; + +public: + class const_iterator + { + /// Directory we are iterating + const Directory* dir; + /// DIR* pointer + void* dirp; + /// dirent structure used for iterating entries + struct dirent* direntbuf; + + public: + // Create an end iterator + const_iterator(); + // Create a begin iterator + const_iterator(const Directory& dir); + // Cleanup properly + ~const_iterator(); + + /// auto_ptr style copy semantics + const_iterator(const const_iterator& i); + const_iterator& operator=(const const_iterator& i); + + /// Move to the next directory entry + const_iterator& operator++(); + + /// @return the current file name + std::string operator*() const; + + bool operator==(const const_iterator& iter) const; + bool operator!=(const const_iterator& iter) const; + }; + + Directory(const std::string& path); + ~Directory(); + + /// Pathname of the directory + const std::string& path() const { return m_path; } + + /// Check if the directory exists + bool exists() const; + + /// Begin iterator + const_iterator begin() const; + + /// End iterator + const_iterator end() const; +}; + +#endif +} +} + +#endif diff --git a/ept/utils/tests-main.cc b/ept/utils/tests-main.cc new file mode 100644 index 0000000..1aec45a --- /dev/null +++ b/ept/utils/tests-main.cc @@ -0,0 +1,139 @@ +#include "tests.h" +#include <signal.h> +#include <cstdlib> +#include <cstring> +#include <exception> + +void signal_to_exception(int) +{ + throw std::runtime_error("killing signal catched"); +} + +int main(int argc,const char* argv[]) +{ + using namespace ept::tests; + + signal(SIGSEGV, signal_to_exception); + signal(SIGILL, signal_to_exception); + +#if 0 + if( (argc == 2 && (! strcmp ("help", argv[1]))) || argc > 3 ) + { + std::cout << "TUT example test application." << std::endl; + std::cout << "Usage: example [regression] | [list] | [ group] [test]" << std::endl; + std::cout << " List all groups: example list" << std::endl; + std::cout << " Run all tests: example regression" << std::endl; + std::cout << " Run one group: example std::auto_ptr" << std::endl; + std::cout << " Run one test: example std::auto_ptr 3" << std::endl;; + } + + // std::cout << "\nFAILURE and EXCEPTION in these tests are FAKE ;)\n\n"; + + tut::runner.get().set_callback(&visi); + + try + { + if( argc == 1 || (argc == 2 && std::string(argv[1]) == "regression") ) + { + tut::runner.get().run_tests(); + } + else if( argc == 2 && std::string(argv[1]) == "list" ) + { + std::cout << "registered test groups:" << std::endl; + tut::groupnames gl = tut::runner.get().list_groups(); + tut::groupnames::const_iterator i = gl.begin(); + tut::groupnames::const_iterator e = gl.end(); + while( i != e ) + { + std::cout << " " << *i << std::endl; + ++i; + } + } + else if( argc == 2 && std::string(argv[1]) != "regression" ) + { + tut::runner.get().run_tests(argv[1]); + } + else if( argc == 3 ) + { + tut::runner.get().run_test(argv[1],::atoi(argv[2])); + } + } + catch( const std::exception& ex ) + { + std::cerr << "tut raised exception: " << ex.what() << std::endl; + } +#endif + + auto& tests = TestRegistry::get(); + + SimpleTestController controller; + + if (const char* whitelist = getenv("TEST_WHITELIST")) + controller.whitelist = whitelist; + + if (const char* blacklist = getenv("TEST_BLACKLIST")) + controller.blacklist = blacklist; + + auto all_results = tests.run_tests(controller); + + unsigned methods_ok = 0; + unsigned methods_failed = 0; + unsigned methods_skipped = 0; + unsigned test_cases_ok = 0; + unsigned test_cases_failed = 0; + + for (const auto& tc_res: all_results) + { + if (!tc_res.fail_setup.empty()) + { + fprintf(stderr, "%s: %s\n", tc_res.test_case.c_str(), tc_res.fail_setup.c_str()); + ++test_cases_failed; + } else { + if (!tc_res.fail_teardown.empty()) + { + fprintf(stderr, "%s: %s\n", tc_res.test_case.c_str(), tc_res.fail_teardown.c_str()); + ++test_cases_failed; + } + else + ++test_cases_ok; + + for (const auto& tm_res: tc_res.methods) + { + if (tm_res.skipped) + ++methods_skipped; + else if (tm_res.is_success()) + ++methods_ok; + else + { + fprintf(stderr, "\n"); + if (tm_res.exception_typeid.empty()) + fprintf(stderr, "%s.%s: %s\n", tm_res.test_case.c_str(), tm_res.test_method.c_str(), tm_res.error_message.c_str()); + else + fprintf(stderr, "%s.%s:[%s] %s\n", tm_res.test_case.c_str(), tm_res.test_method.c_str(), tm_res.exception_typeid.c_str(), tm_res.error_message.c_str()); + for (const auto& frame : tm_res.error_stack) + fprintf(stderr, " %s", frame.format().c_str()); + ++methods_failed; + } + } + } + } + + bool success = true; + + if (test_cases_failed) + { + success = false; + fprintf(stderr, "\n%u/%u test cases had issues initializing or cleaning up\n", + test_cases_failed, test_cases_ok + test_cases_failed); + } + + if (methods_failed) + { + success = false; + fprintf(stderr, "\n%u/%u tests failed\n", methods_failed, methods_ok + methods_failed); + } + else + fprintf(stderr, "%u tests succeeded\n", methods_ok); + + return success ? 0 : 1; +} diff --git a/ept/utils/tests.cc b/ept/utils/tests.cc new file mode 100644 index 0000000..28ea280 --- /dev/null +++ b/ept/utils/tests.cc @@ -0,0 +1,578 @@ +/* + * @author Enrico Zini <enrico@enricozini.org>, Peter Rockai (mornfall) <me@mornfall.net> + * @brief Utility functions for the unit tests + * + * Copyright (C) 2006--2007 Peter Rockai (mornfall) <me@mornfall.net> + * Copyright (C) 2003--2015 Enrico Zini <enrico@debian.org> + */ + +#include "tests.h" +#include "string.h" +#include <fnmatch.h> +#include <cmath> +#include <iomanip> +#include <sys/types.h> +#include <regex.h> + +using namespace std; +using namespace ept; + +const ept::tests::LocationInfo ept_test_location_info; + +namespace ept { +namespace tests { + +/* + * TestStackFrame + */ + +std::string TestStackFrame::format() const +{ + std::stringstream ss; + format(ss); + return ss.str(); +} + +void TestStackFrame::format(std::ostream& out) const +{ + out << file << ":" << line << ":" << call; + if (!local_info.empty()) + out << " [" << local_info << "]"; + out << endl; +} + + +/* + * TestStack + */ + +void TestStack::backtrace(std::ostream& out) const +{ + for (const auto& frame: *this) + frame.format(out); +} + +std::string TestStack::backtrace() const +{ + std::stringstream ss; + backtrace(ss); + return ss.str(); +} + + +/* + * TestFailed + */ + +TestFailed::TestFailed(const std::exception& e) + : message(typeid(e).name()) +{ + message += ": "; + message += e.what(); +} + + +#if 0 +std::string Location::fail_msg(const std::string& error) const +{ + std::stringstream ss; + ss << "test failed at:" << endl; + backtrace(ss); + ss << file << ":" << line << ":error: " << error << endl; + return ss.str(); +} + +std::string Location::fail_msg(std::function<void(std::ostream&)> write_error) const +{ + std::stringstream ss; + ss << "test failed at:" << endl; + backtrace(ss); + ss << file << ":" << line << ":error: "; + write_error(ss); + ss << endl; + return ss.str(); +} +#endif + +std::ostream& LocationInfo::operator()() +{ + str(std::string()); + clear(); + return *this; +} + +/* + * Assertions + */ + +void assert_startswith(const std::string& actual, const std::string& expected) +{ + if (str::startswith(actual, expected)) return; + std::stringstream ss; + ss << "'" << actual << "' does not start with '" << expected << "'"; + throw TestFailed(ss.str()); +} + +void assert_endswith(const std::string& actual, const std::string& expected) +{ + if (str::endswith(actual, expected)) return; + std::stringstream ss; + ss << "'" << actual << "' does not end with '" << expected << "'"; + throw TestFailed(ss.str()); +} + +void assert_contains(const std::string& actual, const std::string& expected) +{ + if (actual.find(expected) != std::string::npos) return; + std::stringstream ss; + ss << "'" << actual << "' does not contain '" << expected << "'"; + throw TestFailed(ss.str()); +} + +void assert_not_contains(const std::string& actual, const std::string& expected) +{ + if (actual.find(expected) == std::string::npos) return; + std::stringstream ss; + ss << "'" << actual << "' contains '" << expected << "'"; + throw TestFailed(ss.str()); +} + +namespace { + +struct Regexp +{ + regex_t compiled; + + Regexp(const char* regex) + { + if (int err = regcomp(&compiled, regex, REG_EXTENDED | REG_NOSUB)) + raise_error(err); + } + ~Regexp() + { + regfree(&compiled); + } + + bool search(const char* s) + { + return regexec(&compiled, s, 0, nullptr, 0) != REG_NOMATCH; + } + + void raise_error(int code) + { + // Get the size of the error message string + size_t size = regerror(code, &compiled, nullptr, 0); + + char* buf = new char[size]; + regerror(code, &compiled, buf, size); + string msg(buf); + delete[] buf; + throw std::runtime_error(msg); + } +}; + +} + +void assert_re_matches(const std::string& actual, const std::string& expected) +{ + Regexp re(expected.c_str()); + if (re.search(actual.c_str())) return; + std::stringstream ss; + ss << "'" << actual << "' does not match '" << expected << "'"; + throw TestFailed(ss.str()); +} + +void assert_not_re_matches(const std::string& actual, const std::string& expected) +{ + Regexp re(expected.c_str()); + if (!re.search(actual.c_str())) return; + std::stringstream ss; + ss << "'" << actual << "' should not match '" << expected << "'"; + throw TestFailed(ss.str()); +} + +void assert_true(std::nullptr_t actual) +{ + throw TestFailed("actual value nullptr is not true"); +}; + +void assert_false(std::nullptr_t actual) +{ +}; + + +static void _actual_must_be_set(const char* actual) +{ + if (!actual) + throw TestFailed("actual value is the null pointer instead of a valid string"); +} + +void ActualCString::operator==(const char* expected) const +{ + if (expected && _actual) + assert_equal<std::string, std::string>(_actual, expected); + else if (!expected && !_actual) + ; + else if (expected) + { + std::stringstream ss; + ss << "actual value is nullptr instead of the expected string \"" << str::encode_cstring(expected) << "\""; + throw TestFailed(ss.str()); + } + else + { + std::stringstream ss; + ss << "actual value is the string \"" << str::encode_cstring(_actual) << "\" instead of nullptr"; + throw TestFailed(ss.str()); + } +} + +void ActualCString::operator==(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_equal<std::string, std::string>(_actual, expected); +} + +void ActualCString::operator!=(const char* expected) const +{ + if (expected && _actual) + assert_not_equal<std::string, std::string>(_actual, expected); + else if (!expected && !_actual) + throw TestFailed("actual and expected values are both nullptr but they should be different"); +} + +void ActualCString::operator!=(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_not_equal<std::string, std::string>(_actual, expected); +} + +void ActualCString::operator<(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_less<std::string, std::string>(_actual, expected); +} + +void ActualCString::operator<=(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_less_equal<std::string, std::string>(_actual, expected); +} + +void ActualCString::operator>(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_greater<std::string, std::string>(_actual, expected); +} + +void ActualCString::operator>=(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_greater_equal<std::string, std::string>(_actual, expected); +} + +void ActualCString::matches(const std::string& re) const +{ + _actual_must_be_set(_actual); + assert_re_matches(_actual, re); +} + +void ActualCString::not_matches(const std::string& re) const +{ + _actual_must_be_set(_actual); + assert_not_re_matches(_actual, re); +} + +void ActualCString::startswith(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_startswith(_actual, expected); +} + +void ActualCString::endswith(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_endswith(_actual, expected); +} + +void ActualCString::contains(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_contains(_actual, expected); +} + +void ActualCString::not_contains(const std::string& expected) const +{ + _actual_must_be_set(_actual); + assert_not_contains(_actual, expected); +} + +void ActualStdString::startswith(const std::string& expected) const +{ + assert_startswith(_actual, expected); +} + +void ActualStdString::endswith(const std::string& expected) const +{ + assert_endswith(_actual, expected); +} + +void ActualStdString::contains(const std::string& expected) const +{ + assert_contains(_actual, expected); +} + +void ActualStdString::not_contains(const std::string& expected) const +{ + assert_not_contains(_actual, expected); +} + +void ActualStdString::matches(const std::string& re) const +{ + assert_re_matches(_actual, re); +} + +void ActualStdString::not_matches(const std::string& re) const +{ + assert_not_re_matches(_actual, re); +} + +void ActualDouble::almost_equal(double expected, unsigned places) const +{ + if (round((_actual - expected) * exp10(places)) == 0.0) + return; + std::stringstream ss; + ss << std::setprecision(places) << fixed << _actual << " is different than the expected " << expected; + throw TestFailed(ss.str()); +} + +void ActualDouble::not_almost_equal(double expected, unsigned places) const +{ + if (round(_actual - expected * exp10(places)) != 0.0) + return; + std::stringstream ss; + ss << std::setprecision(places) << fixed << _actual << " is the same as the expected " << expected; + throw TestFailed(ss.str()); +} + +void ActualFunction::throws(const std::string& what_match) const +{ + bool thrown = false; + try { + _actual(); + } catch (std::exception& e) { + thrown = true; + wassert(actual(e.what()).matches(what_match)); + } + if (!thrown) + throw TestFailed("code did not throw any exception"); +} + +#if 0 +void test_assert_file_exists(WIBBLE_TEST_LOCPRM, const std::string& fname) +{ + if (not sys::fs::exists(fname)) + { + std::stringstream ss; + ss << "file '" << fname << "' does not exists"; + ept_test_location.fail_test(ss.str()); + } +} + +void test_assert_not_file_exists(WIBBLE_TEST_LOCPRM, const std::string& fname) +{ + if (sys::fs::exists(fname)) + { + std::stringstream ss; + ss << "file '" << fname << "' does exists"; + ept_test_location.fail_test(ss.str()); + } +} + +#if 0 +struct TestFileExists +{ + std::string pathname; + bool inverted; + TestFileExists(const std::string& pathname, bool inverted=false) : pathname(pathname), inverted(inverted) {} + TestFileExists operator!() { return TestFileExists(pathname, !inverted); } + void check(EPT_TEST_LOCPRM) const; +}; +#endif + +void TestFileExists::check(WIBBLE_TEST_LOCPRM) const +{ + if (!inverted) + { + if (sys::fs::exists(pathname)) return; + std::stringstream ss; + ss << "file '" << pathname << "' does not exists"; + ept_test_location.fail_test(ss.str()); + } else { + if (not sys::fs::exists(pathname)) return; + std::stringstream ss; + ss << "file '" << pathname << "' exists"; + ept_test_location.fail_test(ss.str()); + } +} +#endif + +TestRegistry& TestRegistry::get() +{ + static TestRegistry* instance = 0; + if (!instance) + instance = new TestRegistry(); + return *instance; +} + +void TestRegistry::register_test_case(TestCase& test_case) +{ + entries.emplace_back(&test_case); +} + +std::vector<TestCaseResult> TestRegistry::run_tests(TestController& controller) +{ + std::vector<TestCaseResult> res; + for (auto& e: entries) + { + e->register_tests(); + // TODO: filter on e.name + res.emplace_back(std::move(e->run_tests(controller))); + } + return res; +} + +TestCaseResult TestCase::run_tests(TestController& controller) +{ + TestCaseResult res(name); + + if (!controller.test_case_begin(*this, res)) + { + res.skipped = true; + controller.test_case_end(*this, res); + return res; + } + + try { + setup(); + } catch (std::exception& e) { + res.set_setup_failed(e); + controller.test_case_end(*this, res); + return res; + } + + for (auto& m: methods) + { + // TODO: filter on m.name + res.add_test_method(run_test(controller, m)); + } + + try { + teardown(); + } catch (std::exception& e) { + res.set_teardown_failed(e); + } + + controller.test_case_end(*this, res); + return res; +} + +TestMethodResult TestCase::run_test(TestController& controller, TestMethod& method) +{ + TestMethodResult res(name, method.name); + + if (!controller.test_method_begin(method, res)) + { + res.skipped = true; + controller.test_method_end(method, res); + return res; + } + + bool run = true; + try { + method_setup(res); + } catch (std::exception& e) { + res.set_setup_exception(e); + run = false; + } + + if (run) + { + try { + method.test_function(); + } catch (TestFailed& e) { + // Location::fail_test() was called + res.set_failed(e); + } catch (std::exception& e) { + // std::exception was thrown + res.set_exception(e); + } catch (...) { + // An unknown exception was thrown + res.set_unknown_exception(); + } + } + + try { + method_teardown(res); + } catch (std::exception& e) { + res.set_teardown_exception(e); + } + + controller.test_method_end(method, res); + return res; +} + +bool SimpleTestController::test_method_should_run(const std::string& fullname) const +{ + if (!whitelist.empty() && fnmatch(whitelist.c_str(), fullname.c_str(), 0) == FNM_NOMATCH) + return false; + + if (!blacklist.empty() && fnmatch(blacklist.c_str(), fullname.c_str(), 0) != FNM_NOMATCH) + return false; + + return true; +} + +bool SimpleTestController::test_case_begin(const TestCase& test_case, const TestCaseResult& test_case_result) +{ + // Skip test case if all its methods should not run + bool should_run = false; + for (const auto& m : test_case.methods) + should_run |= test_method_should_run(test_case.name + "." + m.name); + if (!should_run) return false; + + fprintf(stdout, "%s: ", test_case.name.c_str()); + fflush(stdout); + return true; +} + +void SimpleTestController::test_case_end(const TestCase& test_case, const TestCaseResult& test_case_result) +{ + if (test_case_result.skipped) + ; + else if (test_case_result.is_success()) + fprintf(stdout, "\n"); + else + fprintf(stdout, "\n"); + fflush(stdout); +} + +bool SimpleTestController::test_method_begin(const TestMethod& test_method, const TestMethodResult& test_method_result) +{ + string name = test_method_result.test_case + "." + test_method.name; + return test_method_should_run(name); +} + +void SimpleTestController::test_method_end(const TestMethod& test_method, const TestMethodResult& test_method_result) +{ + if (test_method_result.skipped) + putc('s', stdout); + else if (test_method_result.is_success()) + putc('.', stdout); + else + putc('x', stdout); + fflush(stdout); +} + +} +} diff --git a/ept/utils/tests.h b/ept/utils/tests.h new file mode 100644 index 0000000..3b00a14 --- /dev/null +++ b/ept/utils/tests.h @@ -0,0 +1,804 @@ +#ifndef EPT_TESTS_H +#define EPT_TESTS_H + +/** + * @author Enrico Zini <enrico@enricozini.org>, Peter Rockai (mornfall) <me@mornfall.net> + * @brief Utility functions for the unit tests + * + * Copyright (C) 2006--2007 Peter Rockai (mornfall) <me@mornfall.net> + * Copyright (C) 2003--2013 Enrico Zini <enrico@debian.org> + */ + +#include <string> +#include <sstream> +#include <exception> +#include <functional> +#include <vector> + +namespace ept { +namespace tests { +struct LocationInfo; +} +} + +/* + * These global arguments will be shadowed by local variables in functions that + * implement tests. + * + * They are here to act as default root nodes to fulfill method signatures when + * tests are called from outside other tests. + */ +extern const ept::tests::LocationInfo ept_test_location_info; + +namespace ept { +namespace tests { + +/** + * Add information to the test backtrace for the tests run in the current + * scope. + * + * Example usage: + * \code + * test_function(...) + * { + * EPT_TEST_INFO(info); + * for (unsigned i = 0; i < 10; ++i) + * { + * info() << "Iteration #" << i; + * ... + * } + * } + * \endcode + */ +struct LocationInfo : public std::stringstream +{ + LocationInfo() {} + + /** + * Clear the current information and return the output stream to which new + * information can be sent + */ + std::ostream& operator()(); +}; + +/// Information about one stack frame in the test execution stack +struct TestStackFrame +{ + const char* file; + int line; + const char* call; + std::string local_info; + + TestStackFrame(const char* file, int line, const char* call) + : file(file), line(line), call(call) + { + } + + TestStackFrame(const char* file, int line, const char* call, const LocationInfo& local_info) + : file(file), line(line), call(call), local_info(local_info.str()) + { + } + + std::string format() const; + + void format(std::ostream& out) const; +}; + +struct TestStack : public std::vector<TestStackFrame> +{ + using vector::vector; + + /// Return the formatted backtrace for this location + std::string backtrace() const; + + /// Write the formatted backtrace for this location to \a out + void backtrace(std::ostream& out) const; +}; + +/** + * Exception raised when a test assertion fails, normally by + * Location::fail_test + */ +struct TestFailed : public std::exception +{ + std::string message; + TestStack stack; + + TestFailed(const std::exception& e); + + template<typename ...Args> + TestFailed(const std::exception& e, Args&&... args) + : TestFailed(e) + { + add_stack_info(std::forward<Args>(args)...); + } + + TestFailed(const std::string& message) : message(message) {} + + template<typename ...Args> + TestFailed(const std::string& message, Args&&... args) + : TestFailed(message) + { + add_stack_info(std::forward<Args>(args)...); + } + + const char* what() const noexcept override { return message.c_str(); } + + template<typename ...Args> + void add_stack_info(Args&&... args) { stack.emplace_back(std::forward<Args>(args)...); } +}; + +/** + * Use this to declare a local variable with the given name that will be + * picked up by tests as extra local info + */ +#define EPT_TEST_INFO(name) \ + ept::tests::LocationInfo ept_test_location_info; \ + ept::tests::LocationInfo& name = ept_test_location_info + + +/// Test function that ensures that the actual value is true +template<typename A> +void assert_true(const A& actual) +{ + if (actual) return; + std::stringstream ss; + ss << "actual value " << actual << " is not true"; + throw TestFailed(ss.str()); +}; + +void assert_true(std::nullptr_t actual); + +/// Test function that ensures that the actual value is false +template<typename A> +void assert_false(const A& actual) +{ + if (!actual) return; + std::stringstream ss; + ss << "actual value " << actual << " is not false"; + throw TestFailed(ss.str()); +}; + +void assert_false(std::nullptr_t actual); + +/** + * Test function that ensures that the actual value is the same as a reference + * one + */ +template<typename A, typename E> +void assert_equal(const A& actual, const E& expected) +{ + if (actual == expected) return; + std::stringstream ss; + ss << "value '" << actual << "' is different than the expected '" << expected << "'"; + throw TestFailed(ss.str()); +} + +/** + * Test function that ensures that the actual value is different than a + * reference one + */ +template<typename A, typename E> +void assert_not_equal(const A& actual, const E& expected) +{ + if (actual != expected) return; + std::stringstream ss; + ss << "value '" << actual << "' is not different than the expected '" << expected << "'"; + throw TestFailed(ss.str()); +} + +/// Ensure that the actual value is less than the reference value +template<typename A, typename E> +void assert_less(const A& actual, const E& expected) +{ + if (actual < expected) return; + std::stringstream ss; + ss << "value '" << actual << "' is not less than the expected '" << expected << "'"; + throw TestFailed(ss.str()); +} + +/// Ensure that the actual value is less or equal than the reference value +template<typename A, typename E> +void assert_less_equal(const A& actual, const E& expected) +{ + if (actual <= expected) return; + std::stringstream ss; + ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'"; + throw TestFailed(ss.str()); +} + +/// Ensure that the actual value is greater than the reference value +template<typename A, typename E> +void assert_greater(const A& actual, const E& expected) +{ + if (actual > expected) return; + std::stringstream ss; + ss << "value '" << actual << "' is not greater than the expected '" << expected << "'"; + throw TestFailed(ss.str()); +} + +/// Ensure that the actual value is greather or equal than the reference value +template<typename A, typename E> +void assert_greater_equal(const A& actual, const E& expected) +{ + if (actual >= expected) return; + std::stringstream ss; + ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'"; + throw TestFailed(ss.str()); +} + +/// Ensure that the string \a actual starts with \a expected +void assert_startswith(const std::string& actual, const std::string& expected); + +/// Ensure that the string \a actual ends with \a expected +void assert_endswith(const std::string& actual, const std::string& expected); + +/// Ensure that the string \a actual contains \a expected +void assert_contains(const std::string& actual, const std::string& expected); + +/// Ensure that the string \a actual does not contain \a expected +void assert_not_contains(const std::string& actual, const std::string& expected); + +/** + * Ensure that the string \a actual matches the extended regular expression + * \a expected. + * + * The syntax is that of extended regular expression (see man regex(7) ). + */ +void assert_re_matches(const std::string& actual, const std::string& expected); + +/** + * Ensure that the string \a actual does not match the extended regular + * expression \a expected. + * + * The syntax is that of extended regular expression (see man regex(7) ). + */ +void assert_not_re_matches(const std::string& actual, const std::string& expected); + + +template<class A> +struct Actual +{ + A _actual; + Actual(const A& actual) : _actual(actual) {} + ~Actual() {} + + void istrue() const { assert_true(_actual); } + void isfalse() const { assert_false(_actual); } + template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); } + template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); } + template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); } + template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); } + template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); } + template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); } +}; + +struct ActualCString +{ + const char* _actual; + ActualCString(const char* s) : _actual(s) {} + + void istrue() const { return assert_true(_actual); } + void isfalse() const { return assert_false(_actual); } + void operator==(const char* expected) const; + void operator==(const std::string& expected) const; + void operator!=(const char* expected) const; + void operator!=(const std::string& expected) const; + void operator<(const std::string& expected) const; + void operator<=(const std::string& expected) const; + void operator>(const std::string& expected) const; + void operator>=(const std::string& expected) const; + void startswith(const std::string& expected) const; + void endswith(const std::string& expected) const; + void contains(const std::string& expected) const; + void not_contains(const std::string& expected) const; + void matches(const std::string& re) const; + void not_matches(const std::string& re) const; +}; + +struct ActualStdString : public Actual<std::string> +{ + ActualStdString(const std::string& s) : Actual<std::string>(s) {} + + void startswith(const std::string& expected) const; + void endswith(const std::string& expected) const; + void contains(const std::string& expected) const; + void not_contains(const std::string& expected) const; + void matches(const std::string& re) const; + void not_matches(const std::string& re) const; +}; + +struct ActualDouble : public Actual<double> +{ + using Actual::Actual; + + void almost_equal(double expected, unsigned places) const; + void not_almost_equal(double expected, unsigned places) const; +}; + +template<typename A> +inline Actual<A> actual(const A& actual) { return Actual<A>(actual); } +inline ActualCString actual(const char* actual) { return ActualCString(actual); } +inline ActualCString actual(char* actual) { return ActualCString(actual); } +inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); } +inline ActualDouble actual(double actual) { return ActualDouble(actual); } + +struct ActualFunction : public Actual<std::function<void()>> +{ + using Actual::Actual; + + void throws(const std::string& what_match) const; +}; + +inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); } + + +/** + * Run the given command, raising TestFailed with the appropriate backtrace + * information if it threw an exception. + * + * If the command raises TestFailed, it adds the current stack to its stack + * information. + */ +#define wassert(...) \ + do { try { \ + __VA_ARGS__ ; \ + } catch (TestFailed& e) { \ + e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, ept_test_location_info); \ + throw; \ + } catch (std::exception& e) { \ + throw TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, ept_test_location_info); \ + } } while(0) + +/// Shortcut to check that a given expression returns true +#define wassert_true(...) wassert(actual(__VA_ARGS__).istrue()) + +/// Shortcut to check that a given expression returns false +#define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse()) + +/** + * Call a function returning its result, and raising TestFailed with the + * appropriate backtrace information if it threw an exception. + * + * If the function raises TestFailed, it adds the current stack to its stack + * information. + */ +#define wcallchecked(func) \ + [&]() { try { \ + return func; \ + } catch (TestFailed& e) { \ + e.add_stack_info(__FILE__, __LINE__, #func, ept_test_location_info); \ + throw; \ + } catch (std::exception& e) { \ + throw TestFailed(e, __FILE__, __LINE__, #func, ept_test_location_info); \ + } }() + + +struct TestCase; + +/** + * Result of running a test method. + */ +struct TestMethodResult +{ + /// Name of the test case + std::string test_case; + + /// Name of the test method + std::string test_method; + + /// If non-empty, the test failed with this error + std::string error_message; + + /// Stack frame of where the error happened + TestStack error_stack; + + /// If non-empty, the test raised an exception and this is its type ID + std::string exception_typeid; + + /// True if the test has been skipped + bool skipped = false; + + + TestMethodResult(const std::string& test_case, const std::string& test_method) + : test_case(test_case), test_method(test_method) {} + + void set_failed(TestFailed& e) + { + error_message = e.what(); + error_stack = e.stack; + if (error_message.empty()) + error_message = "test failed with an empty error message"; + } + + void set_exception(std::exception& e) + { + error_message = e.what(); + if (error_message.empty()) + error_message = "test threw an exception with an empty error message"; + exception_typeid = typeid(e).name(); + } + + void set_unknown_exception() + { + error_message = "unknown exception caught"; + } + + void set_setup_exception(std::exception& e) + { + error_message = "[setup failed: "; + error_message += e.what(); + error_message += "]"; + } + + void set_teardown_exception(std::exception& e) + { + error_message = "[teardown failed: "; + error_message += e.what(); + error_message += "]"; + } + + bool is_success() const + { + return error_message.empty(); + } +}; + +/** + * Result of running a whole test case + */ +struct TestCaseResult +{ + /// Name of the test case + std::string test_case; + /// Outcome of all the methods that have been run + std::vector<TestMethodResult> methods; + /// Set to a non-empty string if the setup method of the test case failed + std::string fail_setup; + /// Set to a non-empty string if the teardown method of the test case + /// failed + std::string fail_teardown; + /// Set to true if this test case has been skipped + bool skipped = false; + + TestCaseResult(const std::string& test_case) : test_case(test_case) {} + + void set_setup_failed() + { + fail_setup = "test case setup method threw an unknown exception"; + } + + void set_setup_failed(std::exception& e) + { + fail_setup = "test case setup method threw an exception: "; + fail_setup += e.what(); + } + + void set_teardown_failed() + { + fail_teardown = "test case teardown method threw an unknown exception"; + } + + void set_teardown_failed(std::exception& e) + { + fail_teardown = "test case teardown method threw an exception: "; + fail_teardown += e.what(); + } + + void add_test_method(TestMethodResult&& e) + { + methods.emplace_back(std::move(e)); + } + + bool is_success() const + { + if (!fail_setup.empty() || !fail_teardown.empty()) return false; + for (const auto& m: methods) + if (!m.is_success()) + return false; + return true; + } +}; + +struct TestCase; +struct TestCaseResult; +struct TestMethod; +struct TestMethodResult; + +/** + * Abstract interface for the objects that supervise test execution. + * + * This can be used for printing progress, or to skip test methods or test + * cases. + */ +struct TestController +{ + virtual ~TestController() {} + + /** + * Called before running a test case. + * + * @returns true if the test case should be run, false if it should be skipped + */ + virtual bool test_case_begin(const TestCase& test_case, const TestCaseResult& test_case_result) { return true; } + + /** + * Called after running a test case. + */ + virtual void test_case_end(const TestCase& test_case, const TestCaseResult& test_case_result) {} + + /** + * Called before running a test method. + * + * @returns true if the test method should be run, false if it should be skipped + */ + virtual bool test_method_begin(const TestMethod& test_method, const TestMethodResult& test_method_result) { return true; } + + /** + * Called after running a test method. + */ + virtual void test_method_end(const TestMethod& test_method, const TestMethodResult& test_method_result) {} +}; + +/** + * Simple default implementation of TestController. + * + * It does progress printing to stdout and basic glob-based test method + * filtering. + */ +struct SimpleTestController : public TestController +{ + /// Any method not matching this glob expression will not be run + std::string whitelist; + + /// Any method matching this glob expression will not be run + std::string blacklist; + + bool test_case_begin(const TestCase& test_case, const TestCaseResult& test_case_result) override; + void test_case_end(const TestCase& test_case, const TestCaseResult& test_case_result) override; + bool test_method_begin(const TestMethod& test_method, const TestMethodResult& test_method_result) override; + void test_method_end(const TestMethod& test_method, const TestMethodResult& test_method_result) override; + + bool test_method_should_run(const std::string& fullname) const; +}; + + +/** + * Test registry. + * + * It collects information about all known test cases and takes care of running + * them. + */ +struct TestRegistry +{ + /// All known test cases + std::vector<TestCase*> entries; + + /** + * Register a new test case. + * + * No memory management is done: test_case needs to exist for the whole + * lifetime of TestRegistry. + */ + void register_test_case(TestCase& test_case); + + /** + * Run all the registered tests using the given controller + */ + std::vector<TestCaseResult> run_tests(TestController& controller); + + /// Get the singleton instance of TestRegistry + static TestRegistry& get(); +}; + +/** + * Test method information + */ +struct TestMethod +{ + /// Name of the test method + std::string name; + + /// Main body of the test method + std::function<void()> test_function; + + TestMethod(const std::string& name, std::function<void()> test_function) + : name(name), test_function(test_function) {} +}; + + +/** + * Test case collecting several test methods, and self-registering with the + * singleton instance of TestRegistry. + */ +struct TestCase +{ + /// Name of the test case + std::string name; + + /// All registered test methods + std::vector<TestMethod> methods; + + TestCase(const std::string& name) + : name(name) + { + TestRegistry::get().register_test_case(*this); + } + virtual ~TestCase() {} + + /** + * This will be called before running the test case, to populate it with + * its test methods. + * + * This needs to be reimplemented with a function that will mostly be a + * sequence of calls to add_method(). + */ + virtual void register_tests() = 0; + + /** + * Set up the test case before it is run. + */ + virtual void setup() {} + + /** + * Clean up after the test case is run + */ + virtual void teardown() {} + + /** + * Set up before the test method is run + */ + virtual void method_setup(TestMethodResult&) {} + + /** + * Clean up after the test method is run + */ + virtual void method_teardown(TestMethodResult&) {} + + /** + * Call setup(), run all the tests that have been registered, then + * call teardown(). + * + * Exceptions in setup() and teardown() are caught and reported in + * TestCaseResult. Test are run using run_test(). + */ + virtual TestCaseResult run_tests(TestController& controller); + + /** + * Run a test method. + * + * Call method_setup(), run all the tests that have been registered, then + * call method_teardown(). + * + * Exceptions thrown by the test method are caught and reported in + * TestMethodResult. + * + * Exceptions in method_setup() and method_teardown() are caught and + * reported in TestMethodResult. + */ + virtual TestMethodResult run_test(TestController& controller, TestMethod& method); + + /** + * Register a new test method + */ + template<typename ...Args> + void add_method(const std::string& name, std::function<void()> test_function) + { + methods.emplace_back(name, test_function); + } + + /** + * Register a new test method + */ + template<typename ...Args> + void add_method(const std::string& name, std::function<void()> test_function, Args&&... args) + { + methods.emplace_back(name, test_function, std::forward<Args>(args)...); + } + + /** + * Register a new test metheod, with arguments. + * + * Any extra arguments to the function will be passed to the test method. + */ + template<typename FUNC, typename ...Args> + void add_method(const std::string& name, FUNC test_function, Args&&... args) + { + methods.emplace_back(name, [test_function, args...]() { test_function(args...); }); + } +}; + + +/** + * Base class for test fixtures. + * + * A fixture will have a constructor and a destructor to do setup/teardown, and + * a reset() function to be called inbetween tests. + * + * Fixtures do not need to descend from Fixture: this implementation is + * provided as a default for tests that do not need one, or as a base for + * fixtures that do not need reset(). + */ +struct Fixture +{ + virtual ~Fixture() {} + + // Called before each test + virtual void test_setup() {} + + // Called after each test + virtual void test_teardown() {} +}; + +/** + * Test case that includes a fixture + */ +template<typename FIXTURE> +struct FixtureTestCase : public TestCase +{ + typedef FIXTURE Fixture; + + Fixture* fixture = 0; + std::function<Fixture*()> make_fixture; + + template<typename... Args> + FixtureTestCase(const std::string& name, Args... args) + : TestCase(name) + { + make_fixture = [=]() { return new Fixture(args...); }; + } + + void setup() override + { + TestCase::setup(); + fixture = make_fixture(); + } + + void teardown() override + { + delete fixture; + fixture = 0; + TestCase::teardown(); + } + + void method_setup(TestMethodResult& mr) override + { + TestCase::method_setup(mr); + if (fixture) fixture->test_setup(); + } + + void method_teardown(TestMethodResult& mr) override + { + if (fixture) fixture->test_teardown(); + TestCase::method_teardown(mr); + } + + /** + * Add a method that takes a reference to the fixture as argument. + * + * Any extra arguments to the function will be passed to the test method + * after the fixture. + */ + template<typename FUNC, typename ...Args> + void add_method(const std::string& name, FUNC test_function, Args&&... args) + { + methods.emplace_back(name, [this, test_function, args...] { test_function(*fixture, args...); }); + } +}; + +#if 0 + struct Test + { + std::string name; + std::function<void()> test_func; + }; + + /// Add tests to the test case + virtual void add_tests() {} +#endif + + +} +} + +#endif |