tools: nfdc command line parser
refs #3749
Change-Id: Ief301152212a6d501f0396b2c9834e860ddaf6c5
diff --git a/tools/nfdc/available-commands.cpp b/tools/nfdc/available-commands.cpp
new file mode 100644
index 0000000..3b6a393
--- /dev/null
+++ b/tools/nfdc/available-commands.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "available-commands.hpp"
+#include "status-report.hpp"
+#include "status-main.hpp"
+#include "legacy-nfdc.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+static void
+statusReport(const CommandArguments& ca)
+{
+ int res = 1;
+ ReportFormat fmt = ca.get<ReportFormat>("format", ReportFormat::TEXT);
+ switch (fmt) {
+ case ReportFormat::XML:
+ res = statusMain(std::vector<std::string>{"-x"});
+ break;
+ case ReportFormat::TEXT:
+ res = statusMain(std::vector<std::string>{});
+ break;
+ }
+ exit(res);
+}
+
+static void
+legacyNfdStatus(const CommandArguments& ca)
+{
+ std::vector<std::string> args = ca.get<std::vector<std::string>>("args");
+ int res = statusMain(args);
+ exit(res);
+}
+
+static void
+legacyNfdc(const std::string& subcommand, const CommandArguments& ca)
+{
+ std::vector<std::string> args = ca.get<std::vector<std::string>>("args");
+ int res = legacyNfdcMain(subcommand, args);
+ exit(res);
+}
+
+void
+registerCommands(CommandParser& parser)
+{
+ CommandDefinition defStatusReport("status", "report");
+ defStatusReport
+ .addArg("format", ArgValueType::REPORT_FORMAT, Required::NO, Positional::YES);
+ parser.addCommand(defStatusReport, &statusReport);
+
+ CommandDefinition defLegacyNfdStatus("legacy-nfd-status", "");
+ defLegacyNfdStatus
+ .addArg("args", ArgValueType::ANY, Required::NO, Positional::YES);
+ parser.addCommand(defLegacyNfdStatus, &legacyNfdStatus);
+
+ const std::vector<std::string> legacyNfdcSubcommands{
+ "register",
+ "unregister",
+ "create",
+ "destroy",
+ "set-strategy",
+ "unset-strategy",
+ "add-nexthop",
+ "remove-nexthop"
+ };
+ for (const std::string& subcommand : legacyNfdcSubcommands) {
+ CommandDefinition def(subcommand, "");
+ def.addArg("args", ArgValueType::ANY, Required::NO, Positional::YES);
+ parser.addCommand(def, bind(&legacyNfdc, subcommand, _1));
+ }
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/available-commands.hpp b/tools/nfdc/available-commands.hpp
new file mode 100644
index 0000000..a887eec
--- /dev/null
+++ b/tools/nfdc/available-commands.hpp
@@ -0,0 +1,42 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_AVAILABLE_COMMANDS_HPP
+#define NFD_TOOLS_NFDC_AVAILABLE_COMMANDS_HPP
+
+#include "command-parser.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+void
+registerCommands(CommandParser& parser);
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_AVAILABLE_COMMANDS_HPP
diff --git a/tools/nfdc/command-arguments.hpp b/tools/nfdc/command-arguments.hpp
new file mode 100644
index 0000000..5bf9414
--- /dev/null
+++ b/tools/nfdc/command-arguments.hpp
@@ -0,0 +1,56 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_COMMAND_ARGUMENTS_HPP
+#define NFD_TOOLS_NFDC_COMMAND_ARGUMENTS_HPP
+
+#include "core/common.hpp"
+#include <boost/any.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+/** \brief contains named command arguments
+ */
+class CommandArguments : public std::map<std::string, boost::any>
+{
+public:
+ /** \return the argument value, or a default value if the argument is omitted on command line
+ */
+ template<typename T>
+ T
+ get(const std::string& key, const T& defaultValue = T()) const
+ {
+ auto i = find(key);
+ return i == end() ? defaultValue : boost::any_cast<T>(i->second);
+ }
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_COMMAND_ARGUMENTS_HPP
diff --git a/tools/nfdc/command-definition.cpp b/tools/nfdc/command-definition.cpp
new file mode 100644
index 0000000..cf11928
--- /dev/null
+++ b/tools/nfdc/command-definition.cpp
@@ -0,0 +1,274 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "command-definition.hpp"
+#include "status-report.hpp"
+#include <ndn-cxx/encoding/nfd-constants.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+#include <ndn-cxx/util/logger.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+NDN_LOG_INIT(CommandDefinition);
+
+std::ostream&
+operator<<(std::ostream& os, ArgValueType vt)
+{
+ switch (vt) {
+ case ArgValueType::NONE:
+ return os << "none";
+ case ArgValueType::ANY:
+ return os << "any";
+ case ArgValueType::UNSIGNED:
+ return os << "non-negative integer";
+ case ArgValueType::STRING:
+ return os << "string";
+ case ArgValueType::REPORT_FORMAT:
+ return os << "ReportFormat";
+ case ArgValueType::NAME:
+ return os << "Name";
+ case ArgValueType::FACE_URI:
+ return os << "FaceUri";
+ case ArgValueType::FACE_ID_OR_URI:
+ return os << "FaceId or FaceUri";
+ case ArgValueType::FACE_PERSISTENCY:
+ return os << "FacePersistency";
+ }
+ return os << static_cast<int>(vt);
+}
+
+static std::string
+getMetavarFromType(ArgValueType vt)
+{
+ switch (vt) {
+ case ArgValueType::NONE:
+ return "";
+ case ArgValueType::ANY:
+ return "args";
+ case ArgValueType::UNSIGNED:
+ return "uint";
+ case ArgValueType::STRING:
+ return "str";
+ case ArgValueType::REPORT_FORMAT:
+ return "fmt";
+ case ArgValueType::NAME:
+ return "name";
+ case ArgValueType::FACE_URI:
+ return "uri";
+ case ArgValueType::FACE_ID_OR_URI:
+ return "face";
+ case ArgValueType::FACE_PERSISTENCY:
+ return "persistency";
+ }
+ BOOST_ASSERT(false);
+ return "";
+}
+
+CommandDefinition::CommandDefinition(const std::string& noun, const std::string& verb)
+ : m_noun(noun)
+ , m_verb(verb)
+{
+}
+
+CommandDefinition::~CommandDefinition() = default;
+
+CommandDefinition&
+CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
+ Required isRequired, Positional allowPositional,
+ const std::string& metavar)
+{
+ bool isNew = m_args.emplace(name,
+ Arg{name, valueType, static_cast<bool>(isRequired),
+ metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
+ BOOST_ASSERT(isNew);
+
+ if (static_cast<bool>(isRequired)) {
+ m_requiredArgs.insert(name);
+ }
+
+ if (static_cast<bool>(allowPositional)) {
+ BOOST_ASSERT(valueType != ArgValueType::NONE);
+ m_positionalArgs.push_back(name);
+ }
+ else {
+ BOOST_ASSERT(valueType != ArgValueType::ANY);
+ }
+
+ return *this;
+}
+
+CommandArguments
+CommandDefinition::parse(const std::vector<std::string>& tokens, size_t start) const
+{
+ CommandArguments ca;
+
+ size_t positionalArgIndex = 0;
+ for (size_t i = start; i < tokens.size(); ++i) {
+ const std::string& token = tokens[i];
+
+ // try to parse as named argument
+ auto namedArg = m_args.find(token);
+ if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) {
+ NDN_LOG_TRACE(token << " is a named argument");
+ const Arg& arg = namedArg->second;
+ if (arg.valueType == ArgValueType::NONE) {
+ ca[arg.name] = true;
+ NDN_LOG_TRACE(token << " is a boolean argument");
+ }
+ else if (i + 1 >= tokens.size()) {
+ BOOST_THROW_EXCEPTION(Error(arg.name + ": " + arg.metavar + " is missing"));
+ }
+ else {
+ const std::string& valueToken = tokens[++i];
+ NDN_LOG_TRACE(arg.name << " has value " << valueToken);
+ try {
+ ca[arg.name] = this->parseValue(arg.valueType, valueToken);
+ }
+ catch (const std::exception& e) {
+ NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
+ BOOST_THROW_EXCEPTION(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
+ arg.metavar + " (" + e.what() + ")"));
+ }
+ NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType);
+ }
+
+ // disallow positional arguments after named argument
+ positionalArgIndex = m_positionalArgs.size();
+ continue;
+ }
+
+ // try to parse as positional argument
+ for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) {
+ const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]);
+
+ if (arg.valueType == ArgValueType::ANY) {
+ std::vector<std::string> values;
+ std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values));
+ ca[arg.name] = values;
+ NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name);
+ i = tokens.size();
+ break;
+ }
+
+ try {
+ ca[arg.name] = this->parseValue(arg.valueType, token);
+ NDN_LOG_TRACE(token << " is parsed as value for " << arg.name);
+ break;
+ }
+ catch (const std::exception& e) {
+ if (arg.isRequired) { // the current token must be parsed as the value for arg
+ NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
+ BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name or as " +
+ arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
+ }
+ else {
+ // the current token may be a value for next positional argument
+ NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
+ }
+ }
+ }
+
+ if (positionalArgIndex >= m_positionalArgs.size()) {
+ // for loop has reached the end without finding a match,
+ // which means token is not accepted as a value for positional argument
+ BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name"));
+ }
+
+ // token is accepted; don't parse as the same positional argument again
+ ++positionalArgIndex;
+ }
+
+ for (const std::string& argName : m_requiredArgs) {
+ if (ca.count(argName) == 0) {
+ BOOST_THROW_EXCEPTION(Error(argName + ": required argument is missing"));
+ }
+ }
+
+ return ca;
+}
+
+static ndn::nfd::FacePersistency
+parseFacePersistency(const std::string& s)
+{
+ if (s == "persistent") {
+ return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
+ }
+ if (s == "permanent") {
+ return ndn::nfd::FACE_PERSISTENCY_PERMANENT;
+ }
+ BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized FacePersistency"));
+}
+
+boost::any
+CommandDefinition::parseValue(ArgValueType valueType, const std::string& token) const
+{
+ switch (valueType) {
+ case ArgValueType::NONE:
+ case ArgValueType::ANY:
+ BOOST_ASSERT(false);
+ return boost::any();
+
+ case ArgValueType::UNSIGNED: {
+ // boost::lexical_cast<uint64_t> will accept negative number
+ int64_t v = boost::lexical_cast<int64_t>(token);
+ if (v < 0) {
+ BOOST_THROW_EXCEPTION(std::out_of_range("value is negative"));
+ }
+ return static_cast<uint64_t>(v);
+ }
+
+ case ArgValueType::STRING:
+ return token;
+
+ case ArgValueType::REPORT_FORMAT:
+ return parseReportFormat(token);
+
+ case ArgValueType::NAME:
+ return Name(token);
+
+ case ArgValueType::FACE_URI:
+ return ndn::util::FaceUri(token);
+
+ case ArgValueType::FACE_ID_OR_URI:
+ try {
+ return boost::lexical_cast<uint64_t>(token);
+ }
+ catch (const boost::bad_lexical_cast&) {
+ return ndn::util::FaceUri(token);
+ }
+
+ case ArgValueType::FACE_PERSISTENCY:
+ return parseFacePersistency(token);
+ }
+
+ BOOST_ASSERT(false);
+ return boost::any();
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/command-definition.hpp b/tools/nfdc/command-definition.hpp
new file mode 100644
index 0000000..136f5c4
--- /dev/null
+++ b/tools/nfdc/command-definition.hpp
@@ -0,0 +1,211 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_COMMAND_DEFINITION_HPP
+#define NFD_TOOLS_NFDC_COMMAND_DEFINITION_HPP
+
+#include "command-arguments.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+/** \brief indicates argument value type
+ */
+enum class ArgValueType {
+ /** \brief boolean argument without value
+ *
+ * The argument appears in CommandArguments as bool value 'true'.
+ * It must not be declared as positional.
+ */
+ NONE,
+
+ /** \brief any arguments
+ *
+ * The argument appears in CommandArguments as std::vector<std::string>.
+ * It must be declared as positional, and will consume all subsequent tokens.
+ */
+ ANY,
+
+ /** \brief non-negative integer
+ *
+ * The argument appears in CommandArguments as uint64_t.
+ * Acceptable input range is [0, std::numeric_limits<int64_t>::max()].
+ */
+ UNSIGNED,
+
+ /** \brief arbitrary string
+ *
+ * The argument appears in CommandArguments as std::string.
+ */
+ STRING,
+
+ /** \brief report format 'xml' or 'text'
+ *
+ * The argument appears in CommandArguments as nfd::tools::nfdc::ReportFormat.
+ */
+ REPORT_FORMAT,
+
+ /** \brief Name prefix
+ *
+ * The argument appears in CommandArguments as ndn::Name.
+ */
+ NAME,
+
+ /** \brief FaceUri
+ *
+ * The argument appears in CommandArguments as ndn::util::FaceUri.
+ */
+ FACE_URI,
+
+ /** \brief FaceId or FaceUri
+ *
+ * The argument appears in CommandArguments as either uint64_t or ndn::util::FaceUri.
+ */
+ FACE_ID_OR_URI,
+
+ /** \brief face persistency 'persistent' or 'permanent'
+ *
+ * The argument appears in CommandArguments as ndn::nfd::FacePersistency.
+ */
+ FACE_PERSISTENCY
+};
+
+std::ostream&
+operator<<(std::ostream& os, ArgValueType vt);
+
+/** \brief indicates whether an argument is required
+ */
+enum class Required {
+ NO = false, ///< argument is optional
+ YES = true ///< argument is required
+};
+
+/** \brief indicates whether an argument can be specified as positional
+ */
+enum class Positional {
+ NO = false, ///< argument must be named
+ YES = true ///< argument can be specified as positional
+};
+
+/** \brief declares semantics of a command
+ */
+class CommandDefinition
+{
+public:
+ class Error : public std::invalid_argument
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::invalid_argument(what)
+ {
+ }
+ };
+
+ CommandDefinition(const std::string& noun, const std::string& verb);
+
+ ~CommandDefinition();
+
+ const std::string
+ getNoun() const
+ {
+ return m_noun;
+ }
+
+ const std::string
+ getVerb() const
+ {
+ return m_verb;
+ }
+
+public: // help
+ /** \return one-line synopsis
+ */
+ const std::string&
+ getSynopsis() const
+ {
+ return m_synopsis;
+ }
+
+ /** \brief set one-line synopsis
+ */
+ CommandDefinition&
+ setSynopsis(const std::string& synopsis)
+ {
+ m_synopsis = synopsis;
+ return *this;
+ }
+
+public: // arguments
+ /** \brief declare an argument
+ * \param name argument name, must be unique
+ * \param valueType argument value type
+ * \param isRequired whether the argument is required
+ * \param allowPositional whether the argument value can be specified as positional
+ * \param metavar displayed argument value placeholder
+ */
+ CommandDefinition&
+ addArg(const std::string& name, ArgValueType valueType,
+ Required isRequired = Required::NO,
+ Positional allowPositional = Positional::NO,
+ const std::string& metavar = "");
+
+ /** \brief parse a command line
+ * \param tokens command line tokens
+ * \param start command line start position, after noun and verb
+ * \throw Error command line is invalid
+ */
+ CommandArguments
+ parse(const std::vector<std::string>& tokens, size_t start = 0) const;
+
+private:
+ boost::any
+ parseValue(ArgValueType valueType, const std::string& token) const;
+
+private:
+ std::string m_noun;
+ std::string m_verb;
+
+ std::string m_synopsis;
+
+ struct Arg
+ {
+ std::string name;
+ ArgValueType valueType;
+ bool isRequired;
+ std::string metavar;
+ };
+ typedef std::map<std::string, Arg> ArgMap;
+ ArgMap m_args;
+ std::set<std::string> m_requiredArgs;
+ std::vector<std::string> m_positionalArgs;
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_COMMAND_DEFINITION_HPP
diff --git a/tools/nfdc/command-parser.cpp b/tools/nfdc/command-parser.cpp
new file mode 100644
index 0000000..d91cbc3
--- /dev/null
+++ b/tools/nfdc/command-parser.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "command-parser.hpp"
+#include <ndn-cxx/util/logger.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+NDN_LOG_INIT(CommandParser);
+
+std::ostream&
+operator<<(std::ostream& os, AvailableIn modes)
+{
+ int count = 0;
+
+#define PRINT_BIT(bit, str) \
+ if ((modes & bit) != 0) { \
+ if (++count > 1) { \
+ os << '|'; \
+ } \
+ os << str; \
+ }
+
+ PRINT_BIT(AVAILABLE_IN_ONE_SHOT, "one-shot")
+ PRINT_BIT(AVAILABLE_IN_BATCH, "batch")
+
+#undef PRINT_BIT
+
+ if (count == 0) {
+ os << "none";
+ }
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, ParseMode mode)
+{
+ switch (mode) {
+ case ParseMode::ONE_SHOT:
+ return os << "one-shot";
+ case ParseMode::BATCH:
+ return os << "batch";
+ }
+ return os << static_cast<int>(mode);
+}
+
+CommandParser&
+CommandParser::addCommand(const CommandDefinition& def, const Execute& execute, AvailableIn modes)
+{
+ BOOST_ASSERT(modes != AVAILABLE_IN_NONE);
+ m_commands[{def.getNoun(), def.getVerb()}].reset(new Command{def, execute, modes});
+ return *this;
+}
+
+CommandParser&
+CommandParser::addAlias(const std::string& noun, const std::string& verb, const std::string& verb2)
+{
+ m_commands[{noun, verb2}] = m_commands.at({noun, verb});
+ return *this;
+}
+
+std::tuple<CommandParser::Execute*, CommandArguments>
+CommandParser::parse(const std::vector<std::string>& tokens, ParseMode mode) const
+{
+ BOOST_ASSERT(mode == ParseMode::ONE_SHOT);
+
+ const std::string& noun = tokens.size() > 0 ? tokens[0] : "";
+ const std::string& verb = tokens.size() > 1 ? tokens[1] : "";
+ size_t nameLen = std::min<size_t>(2, tokens.size());
+
+ auto i = m_commands.find({noun, verb});
+ if (i == m_commands.end()) {
+ if (verb.empty()) {
+ i = m_commands.find({noun, "list"});
+ }
+ else {
+ // help, exit, quit, legacy nfdc commands
+ i = m_commands.find({noun, ""});
+ }
+ nameLen = std::min<size_t>(1, tokens.size());
+ }
+ if (i == m_commands.end() || (i->second->modes & static_cast<AvailableIn>(mode)) == 0) {
+ BOOST_THROW_EXCEPTION(Error("no such command: " + noun + " " + verb));
+ }
+
+ const CommandDefinition& def = i->second->def;
+ NDN_LOG_TRACE("found command " << def.getNoun() << " " << def.getVerb());
+
+ return std::make_tuple(&i->second->execute, def.parse(tokens, nameLen));
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/command-parser.hpp b/tools/nfdc/command-parser.hpp
new file mode 100644
index 0000000..7a1dbda
--- /dev/null
+++ b/tools/nfdc/command-parser.hpp
@@ -0,0 +1,114 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_COMMAND_PARSER_HPP
+#define NFD_TOOLS_NFDC_COMMAND_PARSER_HPP
+
+#include "command-definition.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+/** \brief indicates which modes is a command allowed
+ */
+enum AvailableIn : uint8_t {
+ AVAILABLE_IN_NONE = 0,
+ AVAILABLE_IN_ONE_SHOT = 1 << 0,
+ AVAILABLE_IN_BATCH = 1 << 1,
+ AVAILABLE_IN_ALL = 0xff
+};
+
+std::ostream&
+operator<<(std::ostream& os, AvailableIn modes);
+
+/** \brief indicates which mode is the parser operated in
+ */
+enum class ParseMode {
+ ONE_SHOT = AVAILABLE_IN_ONE_SHOT, ///< one-shot mode
+ BATCH = AVAILABLE_IN_BATCH ///< batch mode
+};
+
+std::ostream&
+operator<<(std::ostream& os, ParseMode mode);
+
+/** \brief parses a command
+ */
+class CommandParser : noncopyable
+{
+public:
+ class Error : public std::invalid_argument
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::invalid_argument(what)
+ {
+ }
+ };
+
+ typedef std::function<void(const CommandArguments&)> Execute;
+
+ /** \brief add an available command
+ * \param def command semantics definition
+ * \param execute a function to execute the command
+ * \param modes parse modes this command should be available in, must not be AVAILABLE_IN_NONE
+ */
+ CommandParser&
+ addCommand(const CommandDefinition& def, const Execute& execute, AvailableIn modes = AVAILABLE_IN_ALL);
+
+ /** \brief add an alias "noun verb2" to existing command "noun verb"
+ * \throw std::out_of_range "noun verb" does not exist
+ */
+ CommandParser&
+ addAlias(const std::string& noun, const std::string& verb, const std::string& verb2);
+
+ /** \brief parse a command line
+ * \param tokens command line
+ * \param mode parser mode, must be ParseMode::ONE_SHOT, other modes are not implemented
+ * \throw Error command is not found
+ * \throw CommandDefinition::Error command arguments are invalid
+ */
+ std::tuple<Execute*, CommandArguments>
+ parse(const std::vector<std::string>& tokens, ParseMode mode) const;
+
+private:
+ typedef std::pair<std::string, std::string> CommandName;
+
+ struct Command
+ {
+ CommandDefinition def;
+ Execute execute;
+ AvailableIn modes;
+ };
+
+ std::map<CommandName, shared_ptr<Command>> m_commands;
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_COMMAND_PARSER_HPP
diff --git a/tools/nfdc/main.cpp b/tools/nfdc/main.cpp
index 5bc2596..85fe1e9 100644
--- a/tools/nfdc/main.cpp
+++ b/tools/nfdc/main.cpp
@@ -23,8 +23,8 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "available-commands.hpp"
#include "legacy-nfdc.hpp"
-#include "status-main.hpp"
#include "core/version.hpp"
namespace nfd {
@@ -34,29 +34,35 @@
static int
main(int argc, char** argv)
{
- if (argc < 2) {
+ std::vector<std::string> args(argv + 1, argv + argc);
+
+ if (args.empty() || args[0] == "-h") {
legacyNfdcUsage();
return 0;
}
- std::string subcommand(argv[1]);
- std::vector<std::string> args(argv + 2, argv + argc);
-
- if (subcommand == "-h") {
- legacyNfdcUsage();
- return 0;
- }
-
- if (subcommand == "-V") {
+ if (args[0] == "-V") {
std::cout << NFD_VERSION_BUILD_STRING << std::endl;
return 0;
}
- if (subcommand == "legacy-nfd-status") {
- return statusMain(args);
+ CommandParser parser;
+ registerCommands(parser);
+ CommandParser::Execute* execute = nullptr;
+ CommandArguments ca;
+ try {
+ std::tie(execute, ca) = parser.parse(args, ParseMode::ONE_SHOT);
+ }
+ catch (const std::invalid_argument& e) {
+ std::cerr << e.what() << std::endl;
+ return 1;
}
- return legacyNfdcMain(subcommand, args);
+ ///\todo create Face and KeyChain here
+ (*execute)(ca);
+ ///\todo call processEvents here
+ ///\todo return proper exit code here, instead of using exit() in subcommand
+ return 0;
}
} // namespace nfdc