diff options
author | Enrico Zini <enrico@enricozini.org> | 2015-09-09 22:40:25 +0200 |
---|---|---|
committer | Enrico Zini <enrico@enricozini.org> | 2015-09-09 22:45:28 +0200 |
commit | 3e0b13cf375486da5ee94b836e61fd6a5978f328 (patch) | |
tree | 9d5a5847061031184b9226b88dc5bb8aea0078a1 | |
parent | 74e4ebe74b307d65d9064357cbf80855afbe059b (diff) | |
download | libept-3e0b13cf375486da5ee94b836e61fd6a5978f328.tar.gz |
Removed ept/popcon and removed dependency on tagcoll by bundling the minimum of it that we need
40 files changed, 5150 insertions, 3633 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ddf7bb9..c4b9686 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,6 @@ set( APT_INCLUDES "/usr/include/" CACHE STRING "apt include path" ) set( APT_LINKDIRS "/usr/lib/" CACHE STRING "apt library path" ) set( OPT_FLAGS "-O0 -g" CACHE STRING "optimization level" ) -option( INTERNAL_TAGCOLL "use internal copy of tagcoll" ) option( INTERNAL_WIBBLE "use internal copy of wibble" ) option( RPM "using apt-rpm" ) @@ -38,16 +37,6 @@ else( INTERNAL_WIBBLE ) set( WIBBLE_TEST_CMAKE "${WIBBLE_PREFIX}/share/wibble/test.cmake" ) endif( INTERNAL_WIBBLE ) -if( INTERNAL_TAGCOLL ) - set( TAGCOLL_FOUND ON ) - set( TAGCOLL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR} ) - set( TAGCOLL_LIBRARY_DIRS ${tagcoll_BINARY_DIR} ) - set( TAGCOLL_LIBRARIES "tagcoll2" ) - add_subdirectory( tagcoll ) -else( INTERNAL_TAGCOLL ) - pkg_check_modules( TAGCOLL REQUIRED "libtagcoll2 >= 2.0" ) -endif( INTERNAL_TAGCOLL ) - add_subdirectory( ept ) add_subdirectory( tools ) add_subdirectory( doc ) diff --git a/ept/CMakeLists.txt b/ept/CMakeLists.txt index 96e1746..5f27f14 100644 --- a/ept/CMakeLists.txt +++ b/ept/CMakeLists.txt @@ -1,28 +1,25 @@ project( ept ) include( ${WIBBLE_TEST_CMAKE} ) -file( GLOB src *.cpp debtags/*.cc debtags/maint/*.cc - popcon/*.cc popcon/maint/*.cc apt/*.cc axi/*.cc ) +file( GLOB src *.cpp debtags/*.cc debtags/maint/*.cc debtags/coll/*.cc + apt/*.cc axi/*.cc utils/*.cc ) 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_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} ) +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} ) @@ -69,10 +66,6 @@ add_custom_command( 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 @@ -84,6 +77,5 @@ 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_axi} DESTINATION include/ept/axi ) +install( FILES ${h_utils} DESTINATION include/ept/utils ) diff --git a/ept/debtags/coll/TextFormat.cc b/ept/debtags/coll/TextFormat.cc new file mode 100644 index 0000000..dac5696 --- /dev/null +++ b/ept/debtags/coll/TextFormat.cc @@ -0,0 +1,162 @@ +/* + * 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 <stdexcept> +#include <system_error> + +using namespace std; +using namespace wibble; + +namespace tagcoll { +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; +} + +} +} + +#include "TextFormat.tcc" diff --git a/ept/debtags/coll/TextFormat.h b/ept/debtags/coll/TextFormat.h new file mode 100644 index 0000000..8b4c179 --- /dev/null +++ b/ept/debtags/coll/TextFormat.h @@ -0,0 +1,105 @@ +#ifndef TAGCOLL_TEXTFORMAT_H +#define TAGCOLL_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 <wibble/mixin.h> +#include <wibble/empty.h> +#include <wibble/singleton.h> +//#include <tagcoll/input/base.h> +#include <cstdio> + +//#define TRACE_PARSE + +namespace tagcoll +{ + +namespace textformat +{ + +/** + * TagcollConsumer that serializes its input to an output stream + * + * The format of the output is: + * lines of "comma+space"-separated items, followed by "colon+space", + * followed by the corresponding "comma+space"-separated tags. + * Examples: + * ITEM: + * ITEM: TAG + * ITEM: TAG1, TAG2, TAG3 + * ITEM1, ITEM2, ITEM3: + * ITEM1, ITEM2, ITEM3: TAG1, TAG2, TAG3 + */ +class StdioWriter : public wibble::mixin::OutputIterator<StdioWriter> +{ +protected: + FILE* out; + +public: + StdioWriter(FILE* out) : out(out) {} + + template<typename Items, typename Tags> + StdioWriter& operator=(const std::pair<Items, Tags>& data); +}; + +class OstreamWriter : public wibble::mixin::OutputIterator<OstreamWriter> +{ +protected: + std::ostream& out; + +public: + OstreamWriter(std::ostream& out) : out(out) {} + + template<typename Items, typename Tags> + OstreamWriter& operator=(const std::pair<Items, Tags>& data); +}; + +/** + * 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> + */ +template<typename OUT> +void parse(FILE* in, const std::string& pathname, OUT out); + +} +} + +// vim:set ts=4 sw=4: +#endif diff --git a/ept/debtags/coll/TextFormat.tcc b/ept/debtags/coll/TextFormat.tcc new file mode 100644 index 0000000..2a153c9 --- /dev/null +++ b/ept/debtags/coll/TextFormat.tcc @@ -0,0 +1,176 @@ +/* + * 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 + */ + +#ifndef TAGCOLL_TEXTFORMAT_TCC +#define TAGCOLL_TEXTFORMAT_TCC + +#include <ept/debtags/coll/TextFormat.h> +//#include <tagcoll/patch.h> + +#include <wibble/exception.h> +#include <wibble/empty.h> +#include <wibble/operators.h> + +#include <ostream> + +using namespace std; +using namespace wibble; +using namespace wibble::operators; + +static void printTagset(const std::set<string>& ts, FILE* out) +{ + for (std::set<string>::const_iterator i = ts.begin(); + i != ts.end(); i++) + if (i == ts.begin()) + { + if (fprintf(out, "%s", i->c_str()) < 0) + throw wibble::exception::System("writing tagset"); + } + else + { + if (fprintf(out, ", %s", i->c_str()) < 0) + throw wibble::exception::System("writing tagset"); + } +} + +namespace tagcoll { +namespace textformat { + +inline static void outString(const std::string& str, FILE* out, const char* what) +{ + if (fwrite(str.data(), str.size(), 1, out) != 1) + throw wibble::exception::System(string("writing ") + what); +} + +template<typename Items, typename Tags> +StdioWriter& StdioWriter::operator=(const std::pair<Items, Tags>& data) +{ + for (typename Items::const_iterator i = data.first.begin(); + i != data.first.end(); ++i) + { + if (i != data.first.begin()) + if (fputs(", ", out) == EOF) + throw wibble::exception::System("writing comma after item"); + outString(*i, out, "item"); + } + if (data.second.begin() != data.second.end()) + { + if (fputs(": ", out) == EOF) + throw wibble::exception::System("writing colon after items"); + for (typename Tags::const_iterator i = data.second.begin(); + i != data.second.end(); ++i) + { + if (i != data.second.begin()) + if (fputs(", ", out) == EOF) + throw wibble::exception::System("writing comma after tag"); + outString(*i, out, "tag"); + } + } + if (fputc('\n', out) == EOF) + throw wibble::exception::System("writing newline after tagset"); + return *this; +} + +template<typename Items, typename Tags> +OstreamWriter& OstreamWriter::operator=(const std::pair<Items, Tags>& data) +{ + for (typename Items::const_iterator i = data.first.begin(); + i != data.first.end(); ++i) + { + if (i != data.first.begin()) + out << ", "; + out << *i; + } + if (data.second.begin() != data.second.end()) + { + out << ": "; + for (typename Tags::const_iterator i = data.second.begin(); + i != data.second.end(); ++i) + { + if (i != data.second.begin()) + out << ", "; + out << *i; + } + } + out << endl; + return *this; +} + + + +// item1, item2, item3: tag1, tag2, tag3 + +//#define TRACE_PARSE +template<typename OUT> +void parse(FILE* in, const std::string& pathname, OUT 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 = make_pair(itemset, wibble::Empty<std::string>()); + else + *out = make_pair(itemset, tagset); + ++out; + } + 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); +} + +} +} + +#endif diff --git a/ept/debtags/coll/base.h b/ept/debtags/coll/base.h new file mode 100644 index 0000000..02e59af --- /dev/null +++ b/ept/debtags/coll/base.h @@ -0,0 +1,333 @@ +#ifndef TAGCOLL_COLL_BASE_H +#define TAGCOLL_COLL_BASE_H + +/** \file + * Base mixins for tagged collections + */ + +/* + * 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 <wibble/mixin.h> +#include <vector> + +namespace std { +template<typename A, typename B> class pair; +} + +namespace tagcoll { +namespace coll { + +template<typename T> +class coll_traits; + +/** + * Interface for all collections of tagged items. + * + * \note The point of a collection is to track the tags attached to items, and + * not to store the items themselves. This means that collections are not + * required to keep track of items with no tags. + */ +template<typename Self> +class ReadonlyCollection +{ + const Self& self() const { return *static_cast<const Self*>(this); } + + class CardinalityOrder + { + const Self& coll; + public: + CardinalityOrder(const Self& coll) : coll(coll) {} + bool operator()(const typename coll_traits<Self>::tag_type& t1, const typename coll_traits<Self>::tag_type& t2) + { + // Returns true if t1 precedes t2, and false otherwise + return coll.getCardinality(t1) < coll.getCardinality(t2); + } + }; + + class DiscriminanceOrder + { + const Self& coll; + public: + DiscriminanceOrder(const Self& coll) : coll(coll) {} + bool operator()(const typename coll_traits<Self>::tag_type& t1, const typename coll_traits<Self>::tag_type& t2) + { + // Returns true if t1 precedes t2, and false otherwise + return coll.getDiscriminance(t1) < coll.getDiscriminance(t2); + } + }; + + template<typename COLL> + class RelevanceOrder + { + const COLL& first; + const Self& second; + public: + RelevanceOrder(const COLL& first, const Self& second) + : first(first), second(second) {} + bool operator()(const typename coll_traits<Self>::tag_type& t1, const typename coll_traits<Self>::tag_type& t2); + }; + + /** + * Get the items which are tagged with at least the tag `tag' + * + * \return + * The items found, or an empty set if no items have that tag + */ + //virtual std::set<ITEM> getItemsHavingTag(const TAG& tag) const = 0; + + /** + * Get the tags attached to an item. + * + * \param item + * The item to query + * \return + * The set of tags, or an empty set if the item has no tags or it does + * not exist. + */ + //virtual std::set<TAG> getTagsOfItem(const ITEM& item) const = 0; + +public: + /** + * Check if the collection contains a tag + * + * \param tag + * The tag to look for + * \return + * true if the collection contains tag, false otherwise + */ + bool hasTag(const typename coll_traits<Self>::tag_type& tag) const; + + /** + * Get the tags of item `item'. Return an empty set if `item' does not exist + */ + //std::set<Self::tag_type> getTags(const typename Self::item_type& item) const = 0; + + /** + * Get all the tags attached to the items in a set. + * + * \param items + * The items to query + * \return + * The set of tags, or an empty set if the items have no tags or do not + * exist. + */ + template<typename ITEMS> + typename coll_traits<Self>::tagset_type getTagsOfItems(const ITEMS& items) const; + + /** + * Get the items with tag `tag'. Return an empty set if `tag' does not exist + */ + //std::set<typename Self::item_type> getItems(const TAG& tag) const { return getItemsHavingTag(tag); } + + /** + * 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 + */ + template<typename TAGS> + typename coll_traits<Self>::itemset_type getItemsHavingTags(const TAGS& tags) const; + + /** + * Get the set of all the items that have tags according to this collection + */ + //virtual std::set<Self::item_type> getTaggedItems() const = 0; + + /** + * Get the set of all the tags in this collection + */ + //virtual std::set<Self::tag_type> getAllTags() const = 0; + + /** + * Get all the tags in the collectin, as a vector + */ + std::vector<typename coll_traits<Self>::tag_type> getAllTagsAsVector() const; + + /** + * Get the cardinality of tag `tag' (that is, the number of items who have it) + */ + unsigned int getCardinality(const typename coll_traits<Self>::tag_type& tag) const; + + /** + * Return the discriminance value for this tag, that is, the minimum number + * of packages that would be eliminated by selecting only those tagged with + * this tag or only those not tagged with this tag. + */ + unsigned int getDiscriminance(const typename coll_traits<Self>::tag_type& tag) const + { + return self().getCardinality(tag) < self().tagCount() - self().getCardinality(tag) ? + self().getCardinality(tag) : + self().tagCount() - self().getCardinality(tag); + } + + /** + * Get the set of all tags in this collection that appear in tagsets + * containing `tags' + * + * Example: + * \code + * void refineSelection(const std::set<Tag>& selection) + * { + * std::set<Tag> extraTags = collection.getCompanionTags(selection); + * tagMenu.setAvailableOptions(extraTags); + * } + * \endcode + */ + template<typename TAGS> + typename coll_traits<Self>::tagset_type getCompanionTags(const TAGS& tags) const; + + /** + * Get the related items at the given maximum distance + * + * Examples: + * \code + * // Get the items related to a given one, at the given distance + * std::set<Item> getRelated(const Item& item, int distance) + * { + * std::set<Item> res = collection.getRelatedItems(collection.getTags(item), distance); + * return res - item; + * } + * + * // Get the items related to the given ones, at the given distance + * std::set<Item> getRelated(const std::set<Item>& items, int distance) + * { + * std::set<Item> res = collection.getRelatedItems(collection.getTags(items), distance); + * return res - items; + * } + * + * // Get the related items, increasing the distance until it finds at + * // least 'minimum' items + * std::set<Item> getRelated(const Item& item, int minimum) + * { + * std::set<Tag> tags = collection.getTags(item); + * std::set<Item> res; + * for (int i = 0; i < tags.size() && res.size() < minimum; i++) + * res += collection.getRelatedItems(tags, i); + * return res - item; + * } + * \endcode + */ + template<typename TAGS> + typename coll_traits<Self>::itemset_type getRelatedItems(const TAGS& tags, int maxdistance = 1) const; + + /** + * Output all the contents of the collection to an output iterator + */ + template<typename OUT> + void output(OUT out) const; + + /** + * Send to a consumer all the items which are tagged with at least the + * given tags + */ + template<typename TAGS, typename OUT> + void outputHavingTags(const TAGS& tags, OUT out) const; + + /** + * Get a vector containing all tags in this collection, sorted by + * increasing cardinality + */ + std::vector<typename coll_traits<Self>::tag_type> tagsInCardinalityOrder() const; + + /** + * Get a vector containing all tags in this collection, sorted by + * increasing discriminance value (@see getDiscriminance) + */ + std::vector<typename coll_traits<Self>::tag_type> tagsInDiscriminanceOrder() const; + + /** + * Get a vector containing all tags in this collection, sorted by + * increasing relevance to the filtering applied between coll and this + * collection + */ + template<typename COLL> + std::vector<typename coll_traits<Self>::tag_type> tagsInRelevanceOrder(const COLL& coll) const; +}; + + +/** + * Interface for all collections of tagged items. + * + * \note The point of a collection is to track the tags attached to items, and + * not to store the items themselves. This means that collections are not + * required to keep track of items with no tags. + */ +template<typename Self> +class Collection : public ReadonlyCollection<Self> +{ +//protected: + /* + * Implementation note: to avoid problems with classes implementing only + * some of the virtual methods, they are given different names. The common + * 'comsume' methods are just inlined calls to the right virtual functions, + * and are a way of keeping the unoverridden methods from being hidden. + */ + + //void consumeItemUntagged(const ITEM&) {} + //void consumeItemsUntagged(const std::set<ITEM>&) {} + +public: + //virtual ~Collection() {} + + /** + * Apply a patch to the collection + * + * Example: + * \code + * void perform(const PatchList<ITEM, TAG>& change) + * { + * collection.applyChange(change); + * undo.push_back(change.getReverse()); + * } + * \endcode + */ +// void applyChange( +// const PatchList< +// typename coll_traits<Self>::item_type, +// typename coll_traits<Self>::tag_type>& change); +}; + + +template<typename COLL> +class Inserter : public wibble::mixin::OutputIterator< Inserter<COLL> > +{ + COLL& coll; + +public: + Inserter(COLL& coll) : coll(coll) {} + + template<typename Items, typename Tags> + Inserter<COLL>& operator=(const std::pair<Items, Tags>& data) + { + coll.insert(data.first, data.second); + return *this; + } +}; + +template<typename COLL> +Inserter<COLL> inserter(COLL& target) +{ + return Inserter<COLL>(target); +} + +} +} + +// vim:set ts=4 sw=4: +#endif diff --git a/ept/debtags/coll/base.tcc b/ept/debtags/coll/base.tcc new file mode 100644 index 0000000..546a513 --- /dev/null +++ b/ept/debtags/coll/base.tcc @@ -0,0 +1,191 @@ +#ifndef TAGCOLL_COLL_BASE_TCC +#define TAGCOLL_COLL_BASE_TCC + +/** \file + * Base mixins for tagged collections + */ + +/* + * 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/base.h> +#include <ept/debtags/coll/set.h> +#include <algorithm> + +namespace tagcoll { +namespace coll { + +template<typename T> +class coll_traits; + +template<typename Self> template<typename COLL> +bool ReadonlyCollection<Self>::RelevanceOrder<COLL>::operator()( + const typename coll_traits<Self>::tag_type& t1, + const typename coll_traits<Self>::tag_type& t2) +{ + // New cardinality divided by the square root of the old cardinality. + // The square root is used to downplay the very common tags a bit + int csub1 = second.getCardinality(t1); + float cfull1 = first.getCardinality(t1); + int csub2 = second.getCardinality(t2); + float cfull2 = first.getCardinality(t2); + float rel1 = (float)(csub1 * csub1) / cfull1; + float rel2 = (float)(csub2 * csub2) / cfull2; + + return rel1 < rel2; +// return 10000 * second.getCardinality(t1) / first.getCardinality(t1) +// < 10000 * second.getCardinality(t2) / first.getCardinality(t2); +} + + +template<typename Self> +bool ReadonlyCollection<Self>::hasTag(const typename coll_traits<Self>::tag_type& tag) const +{ + return !self().getItemsHavingTag(tag).empty(); +} + +template<typename Self> template<typename ITEMS> +typename coll_traits<Self>::tagset_type ReadonlyCollection<Self>::getTagsOfItems(const ITEMS& items) const +{ + using namespace wibble::operators; + typename coll_traits<Self>::tagset_type res; + for (typename ITEMS::const_iterator i = items.begin(); + i != items.end(); i++) + res |= self().getTagsOfItem(*i); + return res; +} + +template<typename Self> template<typename TAGS> +typename coll_traits<Self>::itemset_type ReadonlyCollection<Self>::getItemsHavingTags(const TAGS& tags) const +{ + using namespace wibble::operators; + if (tags.empty()) + return typename coll_traits<Self>::itemset_type(); + + typename TAGS::const_iterator i = tags.begin(); + typename coll_traits<Self>::itemset_type res = self().getItemsHavingTag(*i); + + for (++i ; i != tags.end(); ++i) + res &= self().getItemsHavingTag(*i); + + return res; +} + +template<typename Self> +std::vector<typename coll_traits<Self>::tag_type> ReadonlyCollection<Self>::getAllTagsAsVector() const +{ + std::set<typename coll_traits<Self>::tag_type> asSet = self().getAllTags(); + std::vector<typename coll_traits<Self>::tag_type> res; + res.reserve(asSet.size()); + std::copy(asSet.begin(), asSet.end(), back_inserter(res)); + return res; +} + +template<typename Self> +unsigned int ReadonlyCollection<Self>::getCardinality(const typename coll_traits<Self>::tag_type& tag) const +{ + return self().getItemsHavingTag(tag).size(); +} + +template<typename Self> template<typename TAGS> +typename coll_traits<Self>::tagset_type ReadonlyCollection<Self>::getCompanionTags(const TAGS& tags) const +{ + using namespace wibble::operators; + return self().getTagsOfItems(self().getItemsHavingTags(tags)) - tags; +} + +template<typename Self> template<typename TAGS> +typename coll_traits<Self>::itemset_type ReadonlyCollection<Self>::getRelatedItems(const TAGS& tags, int maxdistance) const +{ + using namespace wibble::operators; + + typename coll_traits<Self>::itemset_type packages; + typename coll_traits<Self>::itemset_type res; + + // First get a list of packages that have a non-empty intersection with `tags' + for (typename TAGS::const_iterator i = tags.begin(); i != tags.end(); i++) + packages |= self().getItemsHavingTag(*i); + + // Then keep only those within the given distance + for (typename coll_traits<Self>::itemset_type::const_iterator i = packages.begin(); i != packages.end(); i++) + { + int dist = utils::set_distance(tags, self().getTagsOfItem(*i)); + if (dist >= 0 && dist <= maxdistance) + res |= *i; + } + + return res; +} + +template<typename Self> template<typename OUT> +void ReadonlyCollection<Self>::output(OUT out) const +{ + for (typename Self::const_iterator i = self().begin(); + i != self().end(); ++i) + { + *out = make_pair(wibble::singleton(i->first), i->second); + ++out; + } +} + +template<typename Self> template<typename TAGS, typename OUT> +void ReadonlyCollection<Self>::outputHavingTags(const TAGS& tags, OUT out) const +{ + typename coll_traits<Self>::itemset_type items = self().getItemsHavingTags(tags); + for (typename coll_traits<Self>::itemset_type::const_iterator i = items.begin(); + i != items.end(); ++i) + { + *out = std::make_pair(wibble::singleton(*i), self().getTagsOfItem(*i)); + ++out; + } +} + +template<typename Self> +std::vector<typename coll_traits<Self>::tag_type> ReadonlyCollection<Self>::tagsInCardinalityOrder() const +{ + std::vector<typename coll_traits<Self>::tag_type> res = self().getAllTagsAsVector(); + std::sort(res.begin(), res.end(), CardinalityOrder(self())); + return res; +} + +template<typename Self> +std::vector<typename coll_traits<Self>::tag_type> ReadonlyCollection<Self>::tagsInDiscriminanceOrder() const +{ + std::vector<typename coll_traits<Self>::tag_type> res = self().getAllTagsAsVector(); + std::sort(res.begin(), res.end(), DiscriminanceOrder(self())); + return res; +} + +/** + * Get a vector containing all tags in this collection, sorted by + * increasing relevance to the filtering applied between coll and this + * collection + */ +template<typename Self> template<typename COLL> +std::vector<typename coll_traits<Self>::tag_type> ReadonlyCollection<Self>::tagsInRelevanceOrder(const COLL& coll) const +{ + std::vector<typename coll_traits<Self>::tag_type> res = self().getAllTagsAsVector(); + std::sort(res.begin(), res.end(), RelevanceOrder<COLL>(coll, self())); + return res; +} + +} +} + +// vim:set ts=4 sw=4: +#endif diff --git a/ept/debtags/coll/fast.h b/ept/debtags/coll/fast.h new file mode 100644 index 0000000..7a4c2b8 --- /dev/null +++ b/ept/debtags/coll/fast.h @@ -0,0 +1,138 @@ +#ifndef TAGCOLL_COLL_FAST_H +#define TAGCOLL_COLL_FAST_H + +/** \file + * Fast index for tag data + */ + +/* + * Copyright (C) 2005,2006 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/singleton.h> +#include <ept/debtags/coll/base.h> +#include <set> +#include <map> + +namespace tagcoll { + +namespace coll { +template<typename ITEM, typename TAG> +class Fast; + +template<typename ITEM, typename TAG> +struct coll_traits< Fast<ITEM, TAG> > +{ + typedef ITEM item_type; + typedef TAG tag_type; + typedef std::set<ITEM> itemset_type; + typedef std::set<TAG> tagset_type; +}; + +/** + * In-memory collection with both item->tags and tag->items mappings. + */ +template<class ITEM, class TAG> +class Fast : public coll::Collection< Fast<ITEM, TAG> > +{ +protected: + std::map<ITEM, std::set<TAG> > items; + std::map<TAG, std::set<ITEM> > tags; + +#if 0 + virtual void consumeItem(const ITEM& item, const std::set<TAG>& tags); + virtual void consumeItems(const std::set<ITEM>& items, const std::set<TAG>& tags); + + virtual std::set<ITEM> getItemsHavingTag(const TAG& tag) const; + virtual std::set<TAG> getTagsOfItem(const ITEM& item) const; +#endif + +public: + typedef typename std::map< ITEM, std::set<TAG> >::const_iterator const_iterator; + typedef typename std::map< ITEM, std::set<TAG> >::iterator iterator; + typedef typename std::map< ITEM, std::set<TAG> >::value_type value_type; + + typedef typename std::map< TAG, std::set<ITEM> >::const_iterator const_tag_iterator; + typedef typename std::map< TAG, std::set<ITEM> >::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(); } + + template<typename ITEMS, typename TAGS> + void insert(const ITEMS& items, const TAGS& tags); + + void insert(const wibble::Singleton<ITEM>& item, const std::set<TAG>& tags); + + void insert(const std::set<ITEM>& items, const wibble::Singleton<TAG>& tag); + + void clear() { items.clear(); tags.clear(); } + + std::set<TAG> getTagsOfItem(const ITEM& item) const; + std::set<ITEM> getItemsHavingTag(const TAG& tag) const; + + bool empty() const { return items.empty(); } + + bool hasItem(const ITEM& item) const { return items.find(item) != items.end(); } + bool hasTag(const TAG& tag) const { return tags.find(tag) != tags.end(); } + std::set<ITEM> getTaggedItems() const; + std::set<TAG> getAllTags() const; + std::vector<TAG> getAllTagsAsVector() const; + + unsigned int itemCount() const { return items.size(); } + unsigned int tagCount() const { return tags.size(); } + + /** + * Output all the contents of the reversed collection to an output iterator + */ + template<typename OUT> + void outputReversed(OUT out) const; + +#if 0 + void output(Consumer<ITEM, TAG>& consumer) const; +#endif + + // tag1 implies tag2 if the itemset of tag1 is a subset of the itemset of + // tag2 + std::set<TAG> getTagsImplying(const TAG& tag) const; + + // Return the items which have the exact tagset 'tags' + std::set<ITEM> getItemsExactMatch(const std::set<TAG>& tags) const; + + TAG findTagWithMaxCardinality(size_t& card) const; + + /** + * Return the collection with only those items that have this tag, but with + * the given tag removed + */ + Fast<ITEM, TAG> getChildCollection(const TAG& tag) const; + + void removeTag(const TAG& tag); + void removeTagsWithCardinalityLessThan(size_t card); +}; + +} +} + +// vim:set ts=4 sw=4: +#endif diff --git a/ept/debtags/coll/fast.tcc b/ept/debtags/coll/fast.tcc new file mode 100644 index 0000000..c8731c0 --- /dev/null +++ b/ept/debtags/coll/fast.tcc @@ -0,0 +1,326 @@ +/* + * Fast index for tag data + * + * Copyright (C) 2005,2006 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 TAGCOLL_COLL_FAST_TCC +#define TAGCOLL_COLL_FAST_TCC + +#include <ept/debtags/coll/fast.h> +#include <ept/debtags/coll/set.h> + +#include <wibble/operators.h> + +using namespace std; +using namespace wibble::operators; + +namespace tagcoll { +namespace coll { + +template<class ITEM, class TAG> template<typename ITEMS, typename TAGS> +void Fast<ITEM, TAG>::insert(const ITEMS& items, const TAGS& tags) +{ + using namespace wibble::operators; + + if (tags.empty()) + return; + + for (typename ITEMS::const_iterator i = items.begin(); + i != items.end(); ++i) + { + typename std::map< ITEM, std::set<TAG> >::iterator iter = this->items.find(*i); + if (iter == this->items.end()) + this->items.insert(std::make_pair(*i, std::set<TAG>() | tags)); + else + iter->second |= tags; + } + + for (typename TAGS::const_iterator i = tags.begin(); + i != tags.end(); ++i) + { + typename std::map< TAG, std::set<ITEM> >::iterator iter = this->tags.find(*i); + if (iter == this->tags.end()) + this->tags.insert(std::make_pair(*i, std::set<ITEM>() | items)); + else + iter->second |= items; + } +} + +template<class ITEM, class TAG> +void Fast<ITEM, TAG>::insert(const wibble::Singleton<ITEM>& item, const std::set<TAG>& tags) +{ + using namespace wibble::operators; + + if (tags.empty()) + return; + + typename std::map< ITEM, std::set<TAG> >::iterator iter = this->items.find(*item.begin()); + if (iter == this->items.end()) + this->items.insert(std::make_pair(*item.begin(), tags)); + else + iter->second |= tags; + + for (typename std::set<TAG>::const_iterator i = tags.begin(); + i != tags.end(); ++i) + { + typename std::map< TAG, std::set<ITEM> >::iterator iter = this->tags.find(*i); + if (iter == this->tags.end()) + this->tags.insert(std::make_pair(*i, std::set<ITEM>() | *item.begin())); + else + iter->second |= *item.begin(); + } +} + +template<class ITEM, class TAG> +void Fast<ITEM, TAG>::insert(const std::set<ITEM>& items, const wibble::Singleton<TAG>& tag) +{ + using namespace wibble::operators; + + for (typename std::set<ITEM>::const_iterator i = items.begin(); + i != items.end(); ++i) + { + typename std::map< ITEM, std::set<TAG> >::iterator iter = this->items.find(*i); + if (iter == this->items.end()) + this->items.insert(std::make_pair(*i, std::set<TAG>() | *tag.begin())); + else + iter->second |= *tag.begin(); + } + + typename std::map< TAG, std::set<ITEM> >::iterator iter = this->tags.find(*tag.begin()); + if (iter == this->tags.end()) + this->tags.insert(std::make_pair(*tag.begin(), items)); + else + iter->second |= items; +} + +template<class ITEM, class TAG> +std::set<ITEM> Fast<ITEM, TAG>::getItemsHavingTag(const TAG& tag) const +{ + typename map<TAG, std::set<ITEM> >::const_iterator i = tags.find(tag); + if (i != tags.end()) + return i->second; + else + return std::set<ITEM>(); +} + +template<class ITEM, class TAG> +std::set<TAG> Fast<ITEM, TAG>::getTagsOfItem(const ITEM& item) const +{ + typename map<ITEM, std::set<TAG> >::const_iterator i = items.find(item); + if (i != items.end()) + return i->second; + else + return std::set<TAG>(); +} + +template<class ITEM, class TAG> +std::set<ITEM> Fast<ITEM, TAG>::getTaggedItems() const +{ + std::set<ITEM> res; + for (typename map<ITEM, std::set<TAG> >::const_iterator i = items.begin(); + i != items.end(); i++) + res |= i->first; + return res; +} + +template<class ITEM, class TAG> +std::set<TAG> Fast<ITEM, TAG>::getAllTags() const +{ + std::set<TAG> res; + for (typename map<TAG, std::set<ITEM> >::const_iterator i = tags.begin(); + i != tags.end(); i++) + res |= i->first; + return res; +} + +template<class ITEM, class TAG> +std::vector<TAG> Fast<ITEM, TAG>::getAllTagsAsVector() const +{ + std::vector<TAG> res; + for (typename map<TAG, std::set<ITEM> >::const_iterator i = tags.begin(); + i != tags.end(); i++) + res.push_back(i->first); + return res; +} + +#if 0 +template<class ITEM, class TAG> +void Fast<ITEM, TAG>::output(Consumer<ITEM, TAG>& consumer) const +{ + for (typename map<ITEM, std::set<TAG> >::const_iterator i = items.begin(); + i != items.end(); i++) + consumer.consume(i->first, i->second); +} +#endif + +template<class ITEM, class TAG> template<typename OUT> +void Fast<ITEM, TAG>::outputReversed(OUT out) const +{ + for (typename std::map<TAG, std::set<ITEM> >::const_iterator i = tags.begin(); + i != tags.end(); ++i) + { + *out = make_pair(wibble::singleton(i->first), i->second); + ++out; + } +} + + +template<class ITEM, class TAG> +std::set<TAG> Fast<ITEM, TAG>::getTagsImplying(const TAG& tag) const +{ + // tag1 implies tag2 if the itemset of tag1 is a subset of the itemset of tag2 + std::set<TAG> res; + std::set<ITEM> 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<TAG> tagsToCheck; + for (std::set<ITEM>::const_iterator i = itemsToCheck.begin(); + i != itemsToCheck.end(); ++i) + tagsToCheck |= getTags(*i); + for (std::set<TAG>::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<TAG, std::set<ITEM> >::const_iterator i = tags.begin(); + i != tags.end(); ++i) + if (utils::set_contains(itemsToCheck, getItemsHavingTag(i->first))) + res |= i->first; +#endif + return res - tag; +} + +template<class ITEM, class TAG> +std::set<ITEM> Fast<ITEM, TAG>::getItemsExactMatch(const std::set<TAG>& tags) const +{ + std::set<ITEM> res = this->getItemsHavingTags(tags); + typename std::set<ITEM>::iterator i = res.begin(); + while (i != res.end()) + { + typename std::map<ITEM, std::set<TAG> >::const_iterator t = items.find(*i); + if (t != items.end() && t->second != tags) + { + typename std::set<ITEM>::iterator j = i; + ++i; + res.erase(j); + } else + ++i; + } + return res; +} + +template<class ITEM, class TAG> +TAG Fast<ITEM, TAG>::findTagWithMaxCardinality(size_t& card) const +{ + card = 0; + TAG res = TAG(); + for (typename std::map<TAG, std::set<ITEM> >::const_iterator i = tags.begin(); + i != tags.end(); ++i) + if (i->second.size() > card) + { + card = i->second.size(); + res = i->first; + } + return res; +} + +template<class ITEM, class TAG> +void Fast<ITEM, TAG>::removeTag(const TAG& tag) +{ + typename std::map<TAG, std::set<ITEM> >::iterator itag = tags.find(tag); + for (typename std::set<ITEM>::const_iterator iitemset = itag->second.begin(); + iitemset != itag->second.end(); ++iitemset) + { + typename std::map<ITEM, std::set<TAG> >::iterator iitem = items.find(*iitemset); + iitem->second -= tag; + if (iitem->second.empty()) + items.erase(iitem); + } + tags.erase(itag); +} + +template<class ITEM, class TAG> +Fast<ITEM, TAG> Fast<ITEM, TAG>::getChildCollection(const TAG& tag) const +{ + Fast<ITEM, TAG> res; + + typename std::map<TAG, std::set<ITEM> >::const_iterator itag = tags.find(tag); + for (typename std::set<ITEM>::const_iterator i = itag->second.begin(); + i != itag->second.end(); ++i) + { + typename std::map<ITEM, std::set<TAG> >::const_iterator iitem = items.find(*i); + res.insert(wibble::singleton(*i), iitem->second); + } + + res.removeTag(tag); + return res; +} + +template<class ITEM, class TAG> +void Fast<ITEM, TAG>::removeTagsWithCardinalityLessThan(size_t card) +{ + typename std::map<TAG, std::set<ITEM> >::const_iterator i = tags.begin(); + while (i != tags.end()) + { + if (i->second.size() < card) + { + typename std::map<TAG, std::set<ITEM> >::const_iterator j = i; + ++i; + removeTag(j->first); + } else + ++i; + } +} + + +#if 0 +template<class ITEM, class TAG> +void Fast<ITEM, TAG>::consumeItem(const ITEM& item, const std::set<TAG>& tags) +{ + // Add the tags to the item + items[item] |= tags; + + // Add the item to the tags + for (typename std::set<TAG>::const_iterator i = tags.begin(); i != tags.end(); i++) + this->tags[*i] |= item; +} + +template<class ITEM, class TAG> +void Fast<ITEM, TAG>::consumeItems(const std::set<ITEM>& items, const std::set<TAG>& tags) +{ + for (typename std::set<ITEM>::const_iterator i = items.begin(); i != items.end(); i++) + // Add the tags to the item + this->items[*i] |= tags; + + for (typename std::set<TAG>::const_iterator i = tags.begin(); i != tags.end(); i++) + // Add the items to the tag + this->tags[*i] |= items; +} +#endif + +} +} + +#include <ept/debtags/coll/base.tcc> + +#endif + +// vim:set ts=4 sw=4: diff --git a/ept/debtags/coll/set.h b/ept/debtags/coll/set.h new file mode 100644 index 0000000..f78116f --- /dev/null +++ b/ept/debtags/coll/set.h @@ -0,0 +1,88 @@ +#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 <wibble/operators.h> +#include <set> + +namespace tagcoll { +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(); +} + +} +} + +// vim:set ts=4 sw=4: +#endif diff --git a/ept/debtags/debtags.cc b/ept/debtags/debtags.cc index 4ebf5bf..b800c77 100644 --- a/ept/debtags/debtags.cc +++ b/ept/debtags/debtags.cc @@ -25,14 +25,14 @@ #include <ept/debtags/debtags.h> -#include <tagcoll/patch.h> -#include <tagcoll/coll/simple.h> -#include <tagcoll/input/stdio.h> -#include <tagcoll/TextFormat.h> +//#include <tagcoll/coll/simple.h> +//#include <tagcoll/input/stdio.h> +#include "coll/TextFormat.h" #include <wibble/sys/fs.h> #include <wibble/string.h> +#include <system_error> #include <iostream> #include <sstream> @@ -67,10 +67,18 @@ 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 { + tagcoll::textformat::parse(in, pathname, inserter(*this)); + } catch (...) { + fclose(in); + throw; + } + fclose(in); // Read the timestamp m_timestamp = sys::fs::timestamp(pathname, 0); @@ -86,9 +94,8 @@ string Debtags::pathname() } } -#include <tagcoll/coll/simple.tcc> -#include <tagcoll/coll/fast.tcc> -#include <tagcoll/TextFormat.tcc> +#include "coll/fast.tcc" +#include "coll/TextFormat.tcc" // Explicit template instantiations for our stuff template class tagcoll::coll::Fast<std::string, std::string>; diff --git a/ept/debtags/debtags.h b/ept/debtags/debtags.h index c908f0d..a23b209 100644 --- a/ept/debtags/debtags.h +++ b/ept/debtags/debtags.h @@ -26,8 +26,8 @@ #ifndef EPT_DEBTAGS_DEBTAGS_H #define EPT_DEBTAGS_DEBTAGS_H -#include <tagcoll/coll/base.h> -#include <tagcoll/coll/fast.h> +#include <ept/debtags/coll/base.h> +#include <ept/debtags/coll/fast.h> #include <string> namespace ept { diff --git a/ept/debtags/debtags.test.h b/ept/debtags/debtags.test.h index 54b57eb..8376a9d 100644 --- a/ept/debtags/debtags.test.h +++ b/ept/debtags/debtags.test.h @@ -26,11 +26,6 @@ #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> @@ -199,8 +194,4 @@ struct TestDebtags : DebtagsTestEnvironment }; -#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.cc b/ept/debtags/vocabulary.cc index 504fab2..f2575f9 100644 --- a/ept/debtags/vocabulary.cc +++ b/ept/debtags/vocabulary.cc @@ -20,9 +20,10 @@ #include <ept/debtags/vocabulary.h> #include <ept/debtags/maint/debdbparser.h> -#include <tagcoll/input/memory.h> -#include <tagcoll/input/stdio.h> +#include <wibble/exception.h> #include <wibble/sys/fs.h> +#include <system_error> +#include <cassert> #include <cstring> #include <cstdio> #include <cstdlib> @@ -31,7 +32,6 @@ #include <unistd.h> using namespace std; -using namespace tagcoll; using namespace wibble; namespace ept { @@ -115,8 +115,17 @@ void Vocabulary::load(const std::string& pathname) { if (!sys::fs::exists(pathname)) return; // Read uncompressed data - tagcoll::input::Stdio in(pathname); - read(in); + 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::fs::timestamp(pathname, 0); } @@ -126,7 +135,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 +148,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 +217,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 +252,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() diff --git a/ept/debtags/vocabulary.h b/ept/debtags/vocabulary.h index a5a5fa3..5c5cd32 100644 --- a/ept/debtags/vocabulary.h +++ b/ept/debtags/vocabulary.h @@ -28,12 +28,7 @@ #include <vector> #include <set> #include <map> - -namespace tagcoll { -namespace input { -struct Input; -} -} +#include <cstdio> namespace ept { namespace debtags { @@ -229,11 +224,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 diff --git a/ept/debtags/vocabulary.test.h b/ept/debtags/vocabulary.test.h index 00c152c..4d698ea 100644 --- a/ept/debtags/vocabulary.test.h +++ b/ept/debtags/vocabulary.test.h @@ -20,8 +20,7 @@ #include <wibble/test.h> #include <ept/debtags/vocabulary.h> -#include <tagcoll/utils/set.h> -#include <tagcoll/input/stdio.h> +#include <ept/debtags/coll/set.h> #include "ept/test.h" using namespace std; @@ -52,7 +51,7 @@ struct TestVocabulary : DebtagsTestEnvironment EnvOverride eo("DEBTAGS_VOCABULARY", testfile); Vocabulary tags; assert( tags.hasTag( "works-with::people" ) ); - assert( !tags.hasTag( "works-with::midgets" ) ); + assert( !tags.hasTag( "works-with::foobar" ) ); } Test _4() @@ -60,15 +59,15 @@ struct TestVocabulary : DebtagsTestEnvironment EnvOverride eo("DEBTAGS_VOCABULARY", testfile); Vocabulary tags; const voc::TagData *people = tags.tagData( "works-with::people" ), - *midgets = tags.tagData( "works-with::midgets" ), + *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( people != midgets ); + assert( people != foobar ); assert( people != text ); assert( people != blahg ); - assert( midgets == blahg ); - assert( midgets == midgets ); + assert( foobar == blahg ); + assert( foobar == foobar ); assert( people == people2 ); assert( people == people ); } @@ -78,7 +77,7 @@ struct TestVocabulary : DebtagsTestEnvironment EnvOverride eo("DEBTAGS_VOCABULARY", testfile); Vocabulary tags; std::string a = "works-with::people", - b = "works-with::midgets"; + b = "works-with::foobar"; std::set<std::string> s = tags.tags(), f = tags.tags( "works-with" ), n = tags.tags( "nonsense" ); 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/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/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..6622cae --- /dev/null +++ b/ept/utils/tests.h @@ -0,0 +1,798 @@ +#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) + +/** + * 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 |