tools: make nfdc smarter when the user asks for help

Now 'nfdc foo help', 'nfdc foo --help', and 'nfdc foo -h'
are all accepted as synonyms of 'nfdc help foo'.

Additionally, '--version' can be used in place of '-V'.

Change-Id: I070bb0ea9231a2642c40938377f1e9af2630b34e
Refs: #4503
diff --git a/tools/nfdc/command-parser.cpp b/tools/nfdc/command-parser.cpp
index 74c5202..74ecf36 100644
--- a/tools/nfdc/command-parser.cpp
+++ b/tools/nfdc/command-parser.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2017,  Regents of the University of California,
+ * Copyright (c) 2014-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,6 +25,7 @@
 
 #include "command-parser.hpp"
 #include "format-helpers.hpp"
+
 #include <ndn-cxx/util/logger.hpp>
 
 namespace nfd {
@@ -74,6 +75,7 @@
                           std::underlying_type<AvailableIn>::type modes)
 {
   BOOST_ASSERT(modes != AVAILABLE_IN_NONE);
+
   m_commands[{def.getNoun(), def.getVerb()}].reset(
     new Command{def, execute, static_cast<AvailableIn>(modes)});
 
@@ -95,7 +97,7 @@
 CommandParser::listCommands(const std::string& noun, ParseMode mode) const
 {
   std::vector<const CommandDefinition*> results;
-  for (CommandContainer::const_iterator i : m_commandOrder) {
+  for (auto i : m_commandOrder) {
     const Command& command = *i->second;
     if ((command.modes & static_cast<AvailableIn>(mode)) != 0 &&
         (noun.empty() || noun == command.def.getNoun())) {
@@ -106,7 +108,7 @@
 }
 
 std::tuple<std::string, std::string, CommandArguments, ExecuteCommand>
-CommandParser::parse(const std::vector<std::string>& tokens, ParseMode mode) const
+CommandParser::parse(std::vector<std::string> tokens, ParseMode mode) const
 {
   BOOST_ASSERT(mode == ParseMode::ONE_SHOT);
 
@@ -114,23 +116,42 @@
   const std::string& verb = tokens.size() > 1 ? tokens[1] : "";
   size_t nameLen = std::min<size_t>(2, tokens.size());
 
+  NDN_LOG_TRACE("parse mode=" << mode << " noun=" << noun << " verb=" << verb);
+
   auto i = m_commands.find({noun, verb});
   if (i == m_commands.end()) {
     if (verb.empty()) {
+      NDN_LOG_TRACE("fallback to noun=" << noun << " verb=list");
       i = m_commands.find({noun, "list"});
     }
     else {
       // help, exit, quit commands
+      NDN_LOG_TRACE("fallback to noun=" << noun << " verb=");
       i = m_commands.find({noun, ""});
     }
     nameLen = std::min<size_t>(1, tokens.size());
+
+    if (i == m_commands.end()) {
+      const auto helpStrings = {"help", "--help", "-h"};
+      auto helpIt = std::find_first_of(tokens.begin(), tokens.end(),
+                                       helpStrings.begin(), helpStrings.end());
+      if (helpIt != tokens.end()) {
+        NDN_LOG_TRACE("fallback to noun=help verb=");
+        i = m_commands.find({"help", ""});
+        if (i != m_commands.end()) {
+          tokens.erase(helpIt);
+          nameLen = 0;
+        }
+      }
+    }
   }
+
   if (i == m_commands.end() || (i->second->modes & static_cast<AvailableIn>(mode)) == 0) {
-    BOOST_THROW_EXCEPTION(Error("no such command: " + noun + " " + verb));
+    BOOST_THROW_EXCEPTION(NoSuchCommandError(noun, verb));
   }
 
   const CommandDefinition& def = i->second->def;
-  NDN_LOG_TRACE("found command " << def.getNoun() << " " << def.getVerb());
+  NDN_LOG_TRACE("found command noun=" << def.getNoun() << " verb=" << def.getVerb());
 
   return std::make_tuple(def.getNoun(), def.getVerb(), def.parse(tokens, nameLen), i->second->execute);
 }