repo: visualize contents of the repo

Change-Id: Ifc960d0aaedf816488616aec9ef457b66be9414c
refs #1789
diff --git a/tools/repo-ng-ls.cpp b/tools/repo-ng-ls.cpp
new file mode 100644
index 0000000..be51b34
--- /dev/null
+++ b/tools/repo-ng-ls.cpp
@@ -0,0 +1,204 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California.
+ *
+ * This file is part of NDN repo-ng (Next generation of NDN repository).
+ * See AUTHORS.md for complete list of repo-ng authors and contributors.
+ *
+ * repo-ng 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 3 of the License, or (at your option) any later version.
+ *
+ * repo-ng 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
+ * repo-ng, e.g., in COPYING.md file.  if (not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "../src/common.hpp"
+#include "config.hpp"
+#include <string>
+#include <sqlite3.h>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/info_parser.hpp>
+
+namespace repo {
+
+using namespace ndn::time;
+
+void
+printUsage(const char* programName)
+{
+
+  std::cout
+    << "Usage:\n"
+    << "  " << programName << " [-c <path/to/repo-ng.conf>] [-n] [-h]\n"
+    << "\n"
+    << "List names of Data packets in NDN repository. "
+    << "By default, all names will include the implicit digest of Data packets\n"
+    << "\n"
+    << "Options:\n"
+    << "  -h: show help message\n"
+    << "  -c: set config file path\n"
+    << "  -n: do not show implicit digest\n"
+    << std::endl;
+  ;
+}
+
+class RepoEnumerator
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+public:
+  RepoEnumerator(const std::string& configFile);
+
+  uint64_t
+  enumerate(bool showImplicitDigest);
+
+private:
+  void
+  readConfig(const std::string& configFile);
+
+private:
+  sqlite3* m_db;
+  std::string m_dbPath;
+};
+
+RepoEnumerator::RepoEnumerator(const std::string& configFile)
+{
+  readConfig(configFile);
+  char* errMsg = 0;
+  int rc = sqlite3_open_v2(m_dbPath.c_str(), &m_db,
+                           SQLITE_OPEN_READONLY,
+   #ifdef DISABLE_SQLITE3_FS_LOCKING
+                            "unix-dotfile"
+   #else
+                            0
+   #endif
+                          );
+  if (rc != SQLITE_OK) {
+    throw Error("Database file open failure");
+  }
+  sqlite3_exec(m_db, "PRAGMA synchronous = OFF", 0, 0, &errMsg);
+  sqlite3_exec(m_db, "PRAGMA journal_mode = WAL", 0, 0, &errMsg);
+}
+
+void
+RepoEnumerator::readConfig(const std::string& configFile)
+{
+  if (configFile.empty()) {
+    throw Error("Invalid configuration file name");
+  }
+
+  std::ifstream fin(configFile.c_str());
+  if (!fin.is_open())
+    throw Error("failed to open configuration file '" + configFile + "'");
+
+  using namespace boost::property_tree;
+  ptree propertyTree;
+  try {
+    read_info(fin, propertyTree);
+  }
+  catch (ptree_error& e) {
+    throw Error("failed to read configuration file '" + configFile + "'");
+  }
+  ptree repoConf = propertyTree.get_child("repo");
+  m_dbPath = repoConf.get<std::string>("storage.path");
+  m_dbPath += "/ndn_repo.db";
+}
+
+uint64_t
+RepoEnumerator::enumerate(bool showImplicitDigest)
+{
+  sqlite3_stmt* m_stmt = 0;
+  int rc = SQLITE_DONE;
+  string sql = string("SELECT id, name, keylocatorHash FROM NDN_REPO;");
+  rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &m_stmt, 0);
+  if (rc != SQLITE_OK)
+    throw Error("Initiation Read Entries from Database Prepare error");
+  uint64_t entryNumber = 0;
+  while (true) {
+    rc = sqlite3_step(m_stmt);
+    if (rc == SQLITE_ROW) {
+      Name name;
+      name.wireDecode(Block(sqlite3_column_blob(m_stmt, 1),
+                            sqlite3_column_bytes(m_stmt, 1)));
+      try {
+        if (showImplicitDigest) {
+          std::cout << name << std::endl;
+        }
+        else {
+          std::cout << name.getPrefix(-1) << std::endl;
+        }
+      }
+      catch (...){
+        sqlite3_finalize(m_stmt);
+        throw;
+      }
+      entryNumber++;
+    }
+    else if (rc == SQLITE_DONE) {
+      sqlite3_finalize(m_stmt);
+      break;
+    }
+    else {
+      sqlite3_finalize(m_stmt);
+      throw Error("Initiation Read Entries error");
+    }
+  }
+  return entryNumber;
+}
+
+int
+main(int argc, char** argv)
+{
+  string configPath = DEFAULT_CONFIG_FILE;
+  bool showImplicitDigest = true;
+  int opt;
+  while ((opt = getopt(argc, argv, "hc:n")) != -1) {
+    switch (opt) {
+    case 'h':
+      printUsage(argv[0]);
+      return 0;
+    case 'c':
+      configPath = string(optarg);
+      break;
+    case 'n':
+      showImplicitDigest = false;
+      break;
+    default:
+      break;
+    }
+  }
+
+  RepoEnumerator instance(configPath);
+  uint64_t count = instance.enumerate(showImplicitDigest);
+  std::cerr << "Total number of data = " << count << std::endl;
+  return 0;
+}
+
+} // namespace repo
+
+
+int
+main(int argc, char** argv)
+{
+  try {
+    return repo::main(argc, argv);
+  }
+  catch (std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 2;
+  }
+}