summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnrico Zini <enrico@enricozini.org>2015-09-09 22:40:25 +0200
committerEnrico Zini <enrico@enricozini.org>2015-09-09 22:45:28 +0200
commit3e0b13cf375486da5ee94b836e61fd6a5978f328 (patch)
tree9d5a5847061031184b9226b88dc5bb8aea0078a1
parent74e4ebe74b307d65d9064357cbf80855afbe059b (diff)
downloadlibept-3e0b13cf375486da5ee94b836e61fd6a5978f328.tar.gz
Removed ept/popcon and removed dependency on tagcoll by bundling the minimum of it that we need
-rw-r--r--CMakeLists.txt11
-rw-r--r--ept/CMakeLists.txt20
-rw-r--r--ept/debtags/coll/TextFormat.cc162
-rw-r--r--ept/debtags/coll/TextFormat.h105
-rw-r--r--ept/debtags/coll/TextFormat.tcc176
-rw-r--r--ept/debtags/coll/base.h333
-rw-r--r--ept/debtags/coll/base.tcc191
-rw-r--r--ept/debtags/coll/fast.h138
-rw-r--r--ept/debtags/coll/fast.tcc326
-rw-r--r--ept/debtags/coll/set.h88
-rw-r--r--ept/debtags/debtags.cc25
-rw-r--r--ept/debtags/debtags.h4
-rw-r--r--ept/debtags/debtags.test.h9
-rw-r--r--ept/debtags/expression.test.h82
-rw-r--r--ept/debtags/maint/debdbparser.cc111
-rw-r--r--ept/debtags/maint/debdbparser.h19
-rw-r--r--ept/debtags/vocabulary.cc38
-rw-r--r--ept/debtags/vocabulary.h17
-rw-r--r--ept/debtags/vocabulary.test.h15
-rw-r--r--ept/popcon/local.cc168
-rw-r--r--ept/popcon/local.h87
-rw-r--r--ept/popcon/local.test.h111
-rw-r--r--ept/popcon/maint/path.cc113
-rw-r--r--ept/popcon/maint/path.h125
-rw-r--r--ept/popcon/maint/popconindexer.cc248
-rw-r--r--ept/popcon/maint/popconindexer.h75
-rw-r--r--ept/popcon/maint/sourcedir.cc155
-rw-r--r--ept/popcon/maint/sourcedir.h64
-rw-r--r--ept/popcon/popcon.cc95
-rw-r--r--ept/popcon/popcon.h153
-rw-r--r--ept/popcon/popcon.test.h108
-rw-r--r--ept/test-data/popcon/all-popcon-results.txt.gzbin696390 -> 0 bytes
-rw-r--r--ept/test-data/popcon/popularity-contest1904
-rw-r--r--ept/utils/string.cc437
-rw-r--r--ept/utils/string.h301
-rw-r--r--ept/utils/sys.cc786
-rw-r--r--ept/utils/sys.h468
-rw-r--r--ept/utils/tests-main.cc139
-rw-r--r--ept/utils/tests.cc578
-rw-r--r--ept/utils/tests.h798
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
deleted file mode 100644
index 8b3bf6a..0000000
--- a/ept/test-data/popcon/all-popcon-results.txt.gz
+++ /dev/null
Binary files differ
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