dissect: improve dissect program

This commit improves ndn-dissect program as follows:

* Functions are organized into NdnDissect class.
* The class is placed into namespace ndn::dissect.
* The main function is moved to main.cpp.
* Indentation is provided by ndn::util::IndentedStream.
* Command line parsing uses Boost.Program_options.
* -h and -V command line options are supported.

refs #2848

Change-Id: Ib12584f455910128662ed3f59631d13db52a2cf8
diff --git a/tools/dissect/main.cpp b/tools/dissect/main.cpp
new file mode 100644
index 0000000..fa1a962
--- /dev/null
+++ b/tools/dissect/main.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ndn-dissect.hpp"
+#include "core/version.hpp"
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+namespace po = boost::program_options;
+
+namespace ndn {
+namespace dissect {
+
+void
+usage(std::ostream& os, const std::string& appName, const po::options_description& options)
+{
+  os << "Usage:\n"
+     << "  " << appName << " [input-file] \n"
+     << "\n"
+     << options;
+}
+
+int
+main(int argc, char* argv[])
+{
+  po::options_description visibleOptions;
+  visibleOptions.add_options()
+    ("help,h", "Print help and exit.")
+    ("version,V", "Print version and exit.")
+    ;
+
+  std::string inputFileName;
+  po::options_description hiddenOptions;
+  hiddenOptions.add_options()
+    ("input-file", po::value<std::string>(&inputFileName));
+  ;
+  po::positional_options_description positionalArguments;
+  positionalArguments
+    .add("input-file", -1);
+
+  po::options_description allOptions;
+  allOptions
+    .add(visibleOptions)
+    .add(hiddenOptions)
+    ;
+
+  po::variables_map vm;
+  try {
+    po::store(po::command_line_parser(argc, argv)
+                .options(allOptions)
+                .positional(positionalArguments)
+                .run(),
+              vm);
+    po::notify(vm);
+  }
+  catch (po::error& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
+    usage(std::cerr, argv[0], visibleOptions);
+    return 2;
+  }
+
+  if (vm.count("help") > 0) {
+    usage(std::cout, argv[0], visibleOptions);
+    return 0;
+  }
+
+  if (vm.count("version") > 0) {
+    std::cout << "ndn-dissect " << tools::VERSION << std::endl;
+    return 0;
+  }
+
+  std::ifstream inputFile;
+  std::istream* inputStream;
+
+  if (vm.count("input-file") > 0 && inputFileName != "-") {
+    inputFile.open(inputFileName);
+    inputStream = &inputFile;
+  }
+  else {
+    inputStream = &std::cin;
+  }
+
+  NdnDissect program;
+  program.dissect(std::cout, *inputStream);
+
+  return 0;
+}
+
+} // namespace dissect
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+  return ndn::dissect::main(argc, argv);
+}
diff --git a/tools/dissect/ndn-dissect.cpp b/tools/dissect/ndn-dissect.cpp
index c9d8ece..f37195e 100644
--- a/tools/dissect/ndn-dissect.cpp
+++ b/tools/dissect/ndn-dissect.cpp
@@ -1,5 +1,22 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
+ * Copyright (c) 2014-2015,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
  * Copyright (c) 2013-2014 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
@@ -21,14 +38,16 @@
  * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
  */
 
-#include <ndn-cxx/face.hpp>
-#include <ndn-cxx/encoding/block.hpp>
+#include "ndn-dissect.hpp"
 
-#include <iomanip>
-#include <fstream>
+#include <algorithm>
 #include <map>
 
+#include <ndn-cxx/name-component.hpp>
+#include <ndn-cxx/util/indented-stream.hpp>
+
 namespace ndn {
+namespace dissect {
 
 std::map<uint32_t, std::string> TLV_DICT = {
   {tlv::Interest                     , "Interest"},
@@ -59,35 +78,33 @@
 };
 
 void
-printTypeInfo(uint32_t type)
+NdnDissect::printType(std::ostream& os, uint32_t type)
 {
-  std::cout << type << " (";
+  os << type << " (";
 
   if (TLV_DICT.count(type) != 0) {
-    std::cout << TLV_DICT[type];
+    os << TLV_DICT[type];
   }
   else if (type < tlv::AppPrivateBlock1) {
-    std::cout << "RESERVED_1";
+    os << "RESERVED_1";
   }
   else if (tlv::AppPrivateBlock1 <= type && type < 253) {
-    std::cout << "APP_TAG_1";
+    os << "APP_TAG_1";
   }
   else if (253 <= type && type < tlv::AppPrivateBlock2) {
-    std::cout << "RESERVED_3";
+    os << "RESERVED_3";
   }
   else {
-    std::cout << "APP_TAG_3";
+    os << "APP_TAG_3";
   }
-  std::cout << ")";
+  os << ")";
 }
 
-
 void
-BlockPrinter(const Block& block, const std::string& indent = "")
+NdnDissect::printBlock(std::ostream& os, const Block& block)
 {
-  std::cout << indent;
-  printTypeInfo(block.type());
-  std::cout << " (size: " << block.value_size() << ")";
+  this->printType(os, block.type());
+  os << " (size: " << block.value_size() << ")";
 
   try {
     // if (block.type() != tlv::Content && block.type() != tlv::SignatureValue)
@@ -99,88 +116,33 @@
     // @todo: Figure how to deterministically figure out that value is not recursive TLV block
   }
 
-  if (block.elements().empty())
-    {
-      std::cout << " [[";
-      name::Component(block.value(), block.value_size()).toUri(std::cout);
-      std::cout<< "]]";
-    }
-  std::cout << std::endl;
+  if (block.elements().empty()) {
+    os << " [[";
+    name::Component(block.value(), block.value_size()).toUri(os);
+    os<< "]]";
+  }
+  os << std::endl;
 
-  for (Block::element_const_iterator i = block.elements_begin();
-       i != block.elements_end();
-       ++i)
-    {
-      BlockPrinter(*i, indent+"  ");
-    }
+  util::IndentedStream os2(os, "  ");
+  std::for_each(block.elements_begin(), block.elements_end(),
+    [this, &os2] (const Block& element) {
+      this->printBlock(os2, element);
+    });
 }
 
 void
-HexPrinter(const Block& block, const std::string& indent = "")
-{
-  std::cout << indent;
-  for (Buffer::const_iterator i = block.begin (); i != block.value_begin(); ++i)
-    {
-      std::cout << "0x";
-      std::cout << std::noshowbase << std::hex << std::setw(2) <<
-        std::setfill('0') << static_cast<int>(*i);
-      std::cout << ", ";
-    }
-  std::cout << "\n";
-
-  if (block.elements_size() == 0 && block.value_size() > 0)
-    {
-      std::cout << indent << "    ";
-      for (Buffer::const_iterator i = block.value_begin (); i != block.value_end(); ++i)
-      {
-        std::cout << "0x";
-        std::cout << std::noshowbase << std::hex << std::setw(2) <<
-          std::setfill('0') << static_cast<int>(*i);
-        std::cout << ", ";
-      }
-      std::cout << "\n";
-    }
-  else
-    {
-      for (Block::element_const_iterator i = block.elements_begin();
-           i != block.elements_end();
-           ++i)
-        {
-          HexPrinter(*i, indent+"    ");
-        }
-    }
-}
-
-void
-parseBlocksFromStream(std::istream& is)
+NdnDissect::dissect(std::ostream& os, std::istream& is)
 {
   while (is.peek() != std::char_traits<char>::eof()) {
     try {
       Block block = Block::fromStream(is);
-      BlockPrinter(block, "");
-      // HexPrinter(block, "");
+      this->printBlock(os, block);
     }
     catch (std::exception& e) {
       std::cerr << "ERROR: " << e.what() << std::endl;
     }
   }
-
 }
 
+} // namespace dissect
 } // namespace ndn
-
-int main(int argc, const char *argv[])
-{
-  if (argc == 1 ||
-      (argc == 2 && std::string(argv[1]) == "-"))
-    {
-      ndn::parseBlocksFromStream(std::cin);
-    }
-  else
-    {
-      std::ifstream file(argv[1]);
-      ndn::parseBlocksFromStream(file);
-    }
-
-  return 0;
-}
diff --git a/tools/dissect/ndn-dissect.hpp b/tools/dissect/ndn-dissect.hpp
new file mode 100644
index 0000000..a90b845
--- /dev/null
+++ b/tools/dissect/ndn-dissect.hpp
@@ -0,0 +1,41 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ndn-cxx/encoding/block.hpp>
+#include <fstream>
+
+namespace ndn {
+namespace dissect {
+
+class NdnDissect : noncopyable
+{
+public:
+  void
+  dissect(std::ostream& os, std::istream& is);
+
+private:
+  void
+  printType(std::ostream& os, uint32_t type);
+
+  void
+  printBlock(std::ostream& os, const Block& block);
+};
+
+} // namespace dissect
+} // namespace ndn
diff --git a/tools/dissect/wscript b/tools/dissect/wscript
index 748a292..070ece2 100644
--- a/tools/dissect/wscript
+++ b/tools/dissect/wscript
@@ -2,9 +2,16 @@
 top = '../..'
 
 def build(bld):
-    bld.program(
-        features='cxx',
-        target='../../bin/ndn-dissect',
-        source='ndn-dissect.cpp',
+    bld(features='cxx',
+        name='dissect-objects',
+        source=bld.path.ant_glob('*.cpp', excl='main.cpp'),
+        includes='.',
+        export_includes='.',
         use='core-objects',
         )
+
+    bld(features='cxx cxxprogram',
+        target='../../bin/ndn-dissect',
+        source='main.cpp',
+        use='dissect-objects',
+        )