infoedit

Change-Id: I4d08ee17075f0b0e205817be7dcdb417073df43a
refs: #2964
diff --git a/README.md b/README.md
index 92c7c57..5ecec83 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@
       install_NFD        - install NFD (https://github.com/named-data/NFD)
       install_ndntraffic - install ndn-traffic-generator (https://github.com/named-data/ndn-traffic-generator)
       install_ndntools   - install ndn-tools (https://github.com/named-data/ndn-tools)
+      install_infoedit   - install infoedit
       install_all        - do all of the above
       help               - print this message and exit
 
diff --git a/install_apps.py b/install_apps.py
index 523a1a7..ccf938d 100755
--- a/install_apps.py
+++ b/install_apps.py
@@ -16,6 +16,8 @@
     print "  install_NFD        - install NFD (named-data/NFD)"
     print "  install_ndntraffic - install ndn-traffic-generator (named-data/ndn-traffic-generator)"
     print "  install_ndntools   - install ndn-tools (named-data/ndn-tools)"
+    print "  install_repo       - install repo-ng (named-data/repo-ng)"
+    print "  install_infoedit   - install infoedit"
     print "  install_all        - do all of the above"
     print "  help               - print this message and exit\n"
 
@@ -31,6 +33,8 @@
                      "install_NFD",
                      "install_ndntraffic",
                      "install_ndntools",
+                     "install_repo",
+                     "install_infoedit",
                      "install_all",
                      "help" ]
 
@@ -49,6 +53,8 @@
                 actionList.add("install_NFD")
                 actionList.add("install_ndntraffic")
                 actionList.add("install_ndntools")
+                actionList.add("install_repo")
+                actionList.add("install_infoedit")
             module = __import__("setup_preparation_folder")
             module.run()
             module = __import__("install_dependencies")
@@ -68,6 +74,12 @@
             if "install_ndntools" in actionList:
                 module = __import__("install_ndntools")
                 module.run()
+            if "install_repo" in actionList:
+                module = __import__("install_repo")
+                module.run()
+            if "install_infoedit" in actionList:
+                module = __import__("install_infoedit")
+                module.run()
             print ""
         else:
             usage()
diff --git a/install_helpers/infoedit/README.md b/install_helpers/infoedit/README.md
new file mode 100644
index 0000000..df8a0e5
--- /dev/null
+++ b/install_helpers/infoedit/README.md
@@ -0,0 +1,90 @@
+A tool used to edit NFD configuration files
+===========================================
+
+This tool is majorly developed for use in NFD integration tests.  However, it can also used to
+edit any INFO-formatted files.
+
+Each invocation of `infoedit` tool performs exactly one edit and the file is modified in place.
+To perform multiple edits, `infoedit` tool should be invoked multiple times on the same file.
+
+## Usage ##
+
+    infoedit [-f file] [-s|d|a|r path] [-v value]
+
+OPTIONS:
+
+* `-f file`: specifies a file to edit
+* `-s path`: specifies a property-tree path to modify
+* `-v value`: used with -s path, sets the value at the path
+* `-d path`: deletes all subtrees matching the path
+* `-a path`: adds a subtree at the path; the subtree is read from stdin
+* `-r path`: replaces a subtree at the path; equivalent to -d path followed by -a path
+
+## Sample command lines ##
+
+    ./infoedit -f nfd.conf -s general.user -v ndn.user
+    ./infoedit -f nfd.conf -s tables.strategy_choice./site -v /localhost/nfd/strategy/broadcast
+    ./infoedit -f nfd.conf -d tables.strategy_choice./
+    ./infoedit -f nfd.conf -d authorizations
+    ./infoedit -f nfd.conf -a rib.localhost_security <<<EOT
+      trust-anchor
+      {
+        type any
+      }
+      EOT
+
+### nfd.conf used in above sample ###
+
+    ; comment1
+
+    general ; comment2
+    {
+        user user ; comment3
+        group group
+    }
+
+    tables
+    {
+        strategy_choice
+        {
+            / /localhost/nfd/strategy/best-route
+            /site /localhost/nfd/strategy/broadcast
+        }
+    }
+
+    authorizations
+    {
+        authorize
+        {
+            certfile file1.cert
+        }
+        authorize
+        {
+            certfile file2.cert
+        }
+    }
+
+### nfd.conf after carrying out all commands in above sample ###
+
+    general
+    {
+        user ndn.user
+        group group
+    }
+    tables
+    {
+        strategy_choice
+        {
+            /site /localhost/nfd/strategy/broadcast
+        }
+    }
+    rib
+    {
+        localhost_security
+        {
+            trust-anchor
+            {
+                type any
+            }
+        }
+    }
\ No newline at end of file
diff --git a/install_helpers/infoedit/infoedit.cpp b/install_helpers/infoedit/infoedit.cpp
new file mode 100644
index 0000000..958b54f
--- /dev/null
+++ b/install_helpers/infoedit/infoedit.cpp
@@ -0,0 +1,186 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright 2015, Regents of the University of California.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#include "infoedit.hpp"
+#include <iostream>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+namespace infoedit {
+
+void
+InfoEditor::load(const std::string& fileName)
+{
+  std::ifstream input(fileName);
+  if (!input.good() || !input.is_open()) {
+    throw Error("Failed to open configuration file for parsing");
+  }
+
+  try {
+    boost::property_tree::info_parser::read_info(input, m_info);
+  }
+  catch (boost::property_tree::info_parser::info_parser_error& error) {
+    std::stringstream msg;
+    msg << "Failed to parse configuration file";
+    msg << " " << error.message() << " line " << error.line();
+    throw Error(msg.str());
+  }
+
+  input.close();
+}
+
+InfoEditor&
+InfoEditor::modify(const std::string& section, const std::string& value)
+{
+  m_info.put(section.c_str(), value.c_str());
+  return *this;
+}
+
+InfoEditor&
+InfoEditor::remove(const std::string& section)
+{
+  std::size_t pos = section.find_last_of(".");
+  if (pos == std::string::npos) {
+    m_info.erase(section.c_str());
+  }
+  else {
+    boost::optional<boost::property_tree::ptree&> child =
+      m_info.get_child_optional(section.substr(0, pos));
+    if (child) {
+      child->erase(section.substr(pos + 1));
+    }
+  }
+
+  return *this;
+}
+
+InfoEditor&
+InfoEditor::insert(const std::string& section, std::istream& stream)
+{
+  boost::property_tree::ptree pt;
+  read_info(stream, pt);
+
+  m_info.add_child(section.c_str(), pt);
+
+  return *this;
+}
+
+void
+InfoEditor::save(const std::string& fileName)
+{
+  std::ofstream output(fileName);
+  write_info(output, m_info);
+  output.close();
+}
+
+int
+main(int argc, char** argv)
+{
+  std::string configFile;
+  std::string sectionPath;
+  std::string value;
+
+  namespace po = boost::program_options;
+  po::options_description description("Usage\n"
+                                      "  infoedit [-f file] [-s|d|a|r path] [-v value]\n"
+                                      "Options");
+
+  description.add_options()
+    ("help,h",    "print this help message")
+    ("file,f",    po::value<std::string>(&configFile), "the file to edit")
+    ("section,s", po::value<std::string>(&sectionPath), "the section to modify")
+    ("value,v",   po::value<std::string>(&value), "the value used to modify some section")
+    ("delete,d",  po::value<std::string>(&sectionPath), "the sub tree to delete")
+    ("add,a",     po::value<std::string>(&sectionPath), "adds a sub tree")
+    ("replace,r", po::value<std::string>(&sectionPath), "replace the sub tree")
+    ;
+
+  po::variables_map vm;
+  try {
+      po::store(po::command_line_parser(argc, argv).options(description).run(), vm);
+      po::notify(vm);
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what()
+              << "\n"
+              << description;
+    return 1;
+  }
+
+  if (vm.count("help") > 0) {
+    std::cout << description;
+    return 0;
+  }
+
+  if (vm.count("file") == 0) {
+    std::cerr << "ERROR: the file to edit should be specified"
+              << "\n"
+              << description;
+    return 1;
+  }
+
+  InfoEditor editor;
+  try {
+    editor.load(configFile);
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 1;
+  }
+
+  try {
+    if (vm.count("section") > 0) {
+      if (vm.count("value") == 0) {
+        std::cerr << "ERROR: value must be specified" << std::endl;
+        return 1;
+      }
+
+      editor.modify(sectionPath, value);
+    }
+
+    if (vm.count("delete") > 0) {
+      editor.remove(sectionPath);
+    }
+
+    if (vm.count("add") > 0) {
+      editor.insert(sectionPath, std::cin);
+    }
+
+    if (vm.count("replace") > 0) {
+      editor.remove(sectionPath).insert(sectionPath, std::cin);
+    }
+  }
+  catch (...) {
+    return 1;
+  }
+
+  try {
+    editor.save(configFile);
+  }
+  catch (...) {
+    return 1;
+  }
+
+  return 0;
+}
+
+} // namespace infoedit
+
+int
+main(int argc, char** argv)
+{
+  return infoedit::main(argc, argv);
+}
diff --git a/install_helpers/infoedit/infoedit.hpp b/install_helpers/infoedit/infoedit.hpp
new file mode 100644
index 0000000..6217c03
--- /dev/null
+++ b/install_helpers/infoedit/infoedit.hpp
@@ -0,0 +1,60 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright 2015, Regents of the University of California.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#ifndef INTEGRATION_TESTS_INSTALL_HELPERS_INFOEDIT_INFOEDIT_HPP
+#define INTEGRATION_TESTS_INSTALL_HELPERS_INFOEDIT_INFOEDIT_HPP
+
+#include <fstream>
+#include <iostream>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/info_parser.hpp>
+
+namespace infoedit {
+
+class InfoEditor
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+public:
+  void
+  load(const std::string& fileName);
+
+  InfoEditor&
+  modify(const std::string& section, const std::string& value);
+
+  InfoEditor&
+  remove(const std::string& section);
+
+  InfoEditor&
+  insert(const std::string& section, std::istream& stream);
+
+  void
+  save(const std::string& fileName);
+
+private:
+  boost::property_tree::ptree m_info;
+};
+
+} // namespace infoedit
+
+#endif // INTEGRATION_TESTS_INSTALL_HELPERS_INFOEDIT_INFOEDIT_HPP
diff --git a/install_helpers/install_infoedit.py b/install_helpers/install_infoedit.py
new file mode 100644
index 0000000..737f923
--- /dev/null
+++ b/install_helpers/install_infoedit.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python2
+import os
+
+# Install infoedit
+def run():
+    print "\nINSTALLING infoedit"
+    print "***********************"
+    helper_path = os.path.dirname(os.path.realpath(__file__))
+    cwd_path = os.getcwd()
+    #print helper_path
+    #print cwd_path
+    os.chdir(helper_path)
+    os.chdir("infoedit")
+    os.system("g++ -o infoedit -std=c++0x infoedit.cpp $(pkg-config --cflags --libs libndn-cxx)")
+    os.system("sudo mv infoedit /usr/local/bin/")
+    os.chdir(cwd_path)