]> granicus.if.org Git - icinga2/commitdiff
Improve auto-completion for arguments
authorGunnar Beutner <gunnar.beutner@netways.de>
Tue, 14 Oct 2014 14:45:00 +0000 (16:45 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Tue, 14 Oct 2014 14:45:00 +0000 (16:45 +0200)
fixes #7375

17 files changed:
etc/bash_completion.d/icinga2
etc/icinga2/init.conf.cmake
icinga-app/icinga.cpp
lib/base/clicommand.cpp
lib/base/clicommand.hpp
lib/cli/daemoncommand.cpp
lib/cli/daemoncommand.hpp
lib/cli/featuredisablecommand.cpp
lib/cli/featuredisablecommand.hpp
lib/cli/featureenablecommand.cpp
lib/cli/featureenablecommand.hpp
lib/cli/featurelistcommand.cpp
lib/cli/featurelistcommand.hpp
lib/cli/pkinewcacommand.cpp
lib/cli/pkinewcacommand.hpp
lib/cli/pkinewcertcommand.cpp
lib/cli/pkinewcertcommand.hpp

index 75044b328dad99e50d80781df919e154f93ab658..1b9013e86d417c88179d789c12fde83847728689 100644 (file)
@@ -4,6 +4,7 @@ _icinga2()
   opts="${COMP_WORDS[*]}"
   cur="${COMP_WORDS[COMP_CWORD]}"
   COMPREPLY=($(icinga2 --autocomplete $COMP_CWORD ${COMP_WORDS[*]} < /dev/null))
+  [[ $COMPREPLY = */ ]] && compopt -o nospace
   return 0
 }
 
index 9f57bca82ab1d23bded7562b8b6153f5d34980fc..0772f320b0987279f1f11ad63fde6566798eca08 100644 (file)
@@ -5,3 +5,5 @@
 
 const RunAsUser = "@ICINGA2_USER@"
 const RunAsGroup = "@ICINGA2_GROUP@"
+
+library "cli"
index 89c0df7f61ba66288a31bdb67f59355e7b31cdf5..2a09c9bf63b603bb50226ba141530353d188a9b9 100644 (file)
@@ -48,6 +48,33 @@ SERVICE_STATUS l_SvcStatus;
 SERVICE_STATUS_HANDLE l_SvcStatusHandle;
 #endif /* _WIN32 */
 
+static std::vector<String> LogLevelCompletion(const String& arg)
+{
+       std::vector<String> result;
+       
+       String debugLevel = "debug";
+       if (debugLevel.Find(arg) == 0)
+               result.push_back(debugLevel);
+
+       String noticeLevel = "notice";
+       if (noticeLevel.Find(arg) == 0)
+               result.push_back(noticeLevel);
+
+       String informationLevel = "information";
+       if (informationLevel.Find(arg) == 0)
+               result.push_back(informationLevel);
+
+       String warningLevel = "warning";
+       if (warningLevel.Find(arg) == 0)
+               result.push_back(warningLevel);
+
+       String criticalLevel = "critical";
+       if (criticalLevel.Find(arg) == 0)
+               result.push_back(criticalLevel);
+
+       return result;
+}
+
 int Main(void)
 {
        int argc = Application::GetArgC();
@@ -143,12 +170,16 @@ int Main(void)
        po::positional_options_description positionalDesc;
        positionalDesc.add("arg", -1);
 
+       ArgumentCompletionDescription argDesc;
+       argDesc["include"] = BashArgumentCompletion("directory");
+       argDesc["log-level"] = LogLevelCompletion;
+       
        String cmdname;
        CLICommand::Ptr command;
        po::variables_map vm;
 
        try {
-               CLICommand::ParseCommand(argc, argv, visibleDesc, hiddenDesc, positionalDesc, vm, cmdname, command, autocomplete);
+               CLICommand::ParseCommand(argc, argv, visibleDesc, hiddenDesc, positionalDesc, argDesc, vm, cmdname, command, autocomplete);
        } catch (const std::exception& ex) {
                std::ostringstream msgbuf;
                msgbuf << "Error while parsing command-line options: " << ex.what();
@@ -349,7 +380,7 @@ int Main(void)
        int rc = 1;
 
        if (autocomplete) {
-               CLICommand::ShowCommands(argc, argv, &visibleDesc, &hiddenDesc, true, autoindex);
+               CLICommand::ShowCommands(argc, argv, &visibleDesc, &hiddenDesc, &argDesc, true, autoindex);
                rc = 0;
        } else if (command) {
                std::vector<std::string> args;
index 41d373f2595b2c5b2258f082375d30e8a27c6c31..0dfd358d73d7de6312cd8d8cabe8a37cdc5cdb2a 100644 (file)
@@ -21,6 +21,7 @@
 #include "base/logger_fwd.hpp"
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/trim.hpp>
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/foreach.hpp>
 #include <boost/program_options.hpp>
@@ -32,6 +33,46 @@ namespace po = boost::program_options;
 boost::mutex l_RegistryMutex;
 std::map<std::vector<String>, CLICommand::Ptr> l_Registry;
 
+static std::vector<String> BashArgumentCompletionHelper(const String& type, const String& arg)
+{
+       std::vector<String> result;
+
+#ifndef _WIN32
+       String bashArg = "compgen -A " + Utility::EscapeShellArg(type) + " " + Utility::EscapeShellArg(arg);
+       String cmd = "bash -c " + Utility::EscapeShellArg(bashArg);
+
+       FILE *fp = popen(cmd.CStr(), "r");
+
+       char line[4096];
+       while (fgets(line, sizeof(line), fp)) {
+               String wline = line;
+               boost::algorithm::trim_right_if(wline, boost::is_any_of("\r\n"));
+               result.push_back(wline);
+       }
+       fclose(fp);
+       
+       /* Append a slash if there's only one suggestion and it's a directory */
+       if ((type == "file" || type == "directory") && result.size() == 1) {
+               String path = result[0];
+
+               struct stat statbuf;
+               if (lstat(path.CStr(), &statbuf) >= 0) {
+                       if (S_ISDIR(statbuf.st_mode)) {
+                               result.clear(),
+                               result.push_back(path + "/");
+                       }
+               }
+       }
+#endif /* _WIN32 */
+
+       return result;
+}
+
+ArgumentCompletionCallback icinga::BashArgumentCompletion(const String& type)
+{
+       return boost::bind(BashArgumentCompletionHelper, type, _1);
+}
+
 CLICommand::Ptr CLICommand::GetByName(const std::vector<String>& name)
 {
        boost::mutex::scoped_lock lock(l_RegistryMutex);
@@ -64,8 +105,10 @@ RegisterCLICommandHelper::RegisterCLICommandHelper(const String& name, const CLI
 }
 
 bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& visibleDesc,
-    po::options_description& hiddenDesc, po::positional_options_description& positionalDesc, po::variables_map& vm,
-    String& cmdname, CLICommand::Ptr& command, bool autocomplete)
+    po::options_description& hiddenDesc,
+    po::positional_options_description& positionalDesc,
+    ArgumentCompletionDescription& argCompletionDesc,
+    po::variables_map& vm, String& cmdname, CLICommand::Ptr& command, bool autocomplete)
 {
        boost::mutex::scoped_lock lock(l_RegistryMutex);
 
@@ -104,7 +147,7 @@ found_command:
        po::options_description vdesc("Command options");
 
        if (command)
-               command->InitParameters(vdesc, hiddenDesc);
+               command->InitParameters(vdesc, hiddenDesc, argCompletionDesc);
 
        visibleDesc.add(vdesc);
 
@@ -122,7 +165,8 @@ found_command:
 }
 
 void CLICommand::ShowCommands(int argc, char **argv, po::options_description *visibleDesc,
-    po::options_description *hiddenDesc, bool autocomplete, int autoindex)
+    po::options_description *hiddenDesc, ArgumentCompletionDescription *argCompletionDesc,
+    bool autocomplete, int autoindex)
 {
        boost::mutex::scoped_lock lock(l_RegistryMutex);
 
@@ -198,13 +242,47 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi
        }
 
        if (command && autocomplete) {
-               po::options_description vdesc("Command options");
-
-               if (command)
-                       command->InitParameters(vdesc, *hiddenDesc);
+               String aname, prefix, pword;
+               ArgumentCompletionDescription::const_iterator it;
+               const po::option_description *odesc;
+       
+               if (autoindex - 2 >= 0 && strcmp(argv[autoindex - 1], "=") == 0 && strstr(argv[autoindex - 2], "--") == argv[autoindex - 2]) {
+                       aname = argv[autoindex - 2] + 2;
+                       pword = aword;
+               } else if (autoindex - 1 >= 0 && argv[autoindex - 1][0] == '-' && argv[autoindex - 1][1] == '-') {
+                       aname = argv[autoindex - 1] + 2;
+                       pword = aword;
+                       
+                       if (pword == "=")
+                               pword = "";
+               } else if (aword.GetLength() > 1 && aword[0] == '-' && aword[1] != '-') {
+                       aname = aword.SubStr(1, 1);
+                       prefix = aword.SubStr(0, 2);
+                       pword = aword.SubStr(2);
+               } else {
+                       goto complete_option;
+               }
 
-               visibleDesc->add(vdesc);
+               odesc = visibleDesc->find_nothrow(aname, false);
+
+               if (!odesc)
+                       return;
+
+               if (odesc->semantic()->min_tokens() == 0)
+                       goto complete_option;
+                       
+               it = argCompletionDesc->find(odesc->long_name());
+               
+               if (it == argCompletionDesc->end())
+                       return;
+               
+               BOOST_FOREACH(const String& suggestion, it->second(pword)) {
+                       std::cout << prefix << suggestion << "\n";
+               }
+               
+               return;
 
+complete_option:
                BOOST_FOREACH(const shared_ptr<po::option_description>& odesc, visibleDesc->options()) {
                        String cname = "--" + odesc->long_name();
 
index 9311607014ff53235e92d2ef3e6e5d1f69c79e73..5abdea6bc66ddea93ca6f38fc281e1a88b6d1537 100644 (file)
 namespace icinga
 {
 
+typedef boost::function<std::vector<String> (const String&)> ArgumentCompletionCallback;
+typedef std::map<String, ArgumentCompletionCallback> ArgumentCompletionDescription;
+
+I2_BASE_API ArgumentCompletionCallback BashArgumentCompletion(const String& type);
+
 /**
  * A CLI command.
  *
@@ -41,7 +46,9 @@ public:
 
        virtual String GetDescription(void) const = 0;
        virtual String GetShortDescription(void) const = 0;
-       virtual void InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const = 0;
+       virtual void InitParameters(boost::program_options::options_description& visibleDesc,
+           boost::program_options::options_description& hiddenDesc,
+           ArgumentCompletionDescription& argCompletionDesc) const = 0;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const = 0;
 
        static CLICommand::Ptr GetByName(const std::vector<String>& name);
@@ -51,10 +58,15 @@ public:
        static bool ParseCommand(int argc, char **argv, boost::program_options::options_description& visibleDesc,
             boost::program_options::options_description& hiddenDesc,
             boost::program_options::positional_options_description& positionalDesc,
+            ArgumentCompletionDescription& argCompletionDesc,
             boost::program_options::variables_map& vm, String& cmdname,
             CLICommand::Ptr& command, bool autocomplete);
-       static void ShowCommands(int argc, char **argv, boost::program_options::options_description *visibleDesc = NULL,
-           boost::program_options::options_description *hiddenDesc = NULL, bool autocomplete = false, int autoindex = -1);
+
+       static void ShowCommands(int argc, char **argv,
+            boost::program_options::options_description *visibleDesc = NULL,
+           boost::program_options::options_description *hiddenDesc = NULL,
+           ArgumentCompletionDescription *argCompletionDesc = NULL,
+           bool autocomplete = false, int autoindex = -1);
 };
 
 /**
index 24286968a2fc366aeddbddbfc5bb354cb926f12c..f53e972f4f9a9fe89869898d873ed4f769189293 100644 (file)
@@ -276,7 +276,8 @@ String DaemonCommand::GetShortDescription(void) const
 }
 
 void DaemonCommand::InitParameters(boost::program_options::options_description& visibleDesc,
-    boost::program_options::options_description& hiddenDesc) const
+    boost::program_options::options_description& hiddenDesc,
+    ArgumentCompletionDescription& argCompletionDesc) const
 {
        visibleDesc.add_options()
                ("config,c", po::value<std::vector<std::string> >(), "parse a configuration file")
@@ -292,6 +293,9 @@ void DaemonCommand::InitParameters(boost::program_options::options_description&
        hiddenDesc.add_options()
                ("reload-internal", po::value<int>(), "used internally to implement config reload: do not call manually, send SIGHUP instead");
 #endif /* _WIN32 */
+
+       argCompletionDesc["config"] = BashArgumentCompletion("file");
+       argCompletionDesc["errorlog"] = BashArgumentCompletion("file");
 }
 
 /**
index 6de3bf34fdfb18df61dce1a01faf3567ba4905c0..3341bcb30feb0c9755131d69f9a5d2d8c76f1b9b 100644 (file)
@@ -40,7 +40,8 @@ public:
        virtual String GetDescription(void) const;
        virtual String GetShortDescription(void) const;
        virtual void InitParameters(boost::program_options::options_description& visibleDesc,
-           boost::program_options::options_description& hiddenDesc) const;
+           boost::program_options::options_description& hiddenDesc,
+           ArgumentCompletionDescription& argCompletionDesc) const;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
 };
 
index bf20fac08f49ae9ae87cdfe9c2b8cb2e4fdcb1b2..725c0c5c12b9525730c6ba6b816699c853a3c5bf 100644 (file)
@@ -45,7 +45,8 @@ String FeatureDisableCommand::GetShortDescription(void) const
 }
 
 void FeatureDisableCommand::InitParameters(boost::program_options::options_description& visibleDesc,
-    boost::program_options::options_description& hiddenDesc) const
+    boost::program_options::options_description& hiddenDesc,
+    ArgumentCompletionDescription& argCompletionDesc) const
 {
         /* Command doesn't support any parameters. */
 }
index fcb6f783989402259ab6e3c90a9e39722db91706..32d9e8c4d8fa97f239a022ee9ef7c6fe5bdda900 100644 (file)
@@ -39,7 +39,8 @@ public:
        virtual String GetDescription(void) const;
        virtual String GetShortDescription(void) const;
        virtual void InitParameters(boost::program_options::options_description& visibleDesc,
-           boost::program_options::options_description& hiddenDesc) const;
+           boost::program_options::options_description& hiddenDesc,
+           ArgumentCompletionDescription& argCompletionDesc) const;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
 
 };
index 4e7774aea08d164b4d9bad0ed52a72ad5dede171..13b8b87c90e50e12a317e36fbf611510ff3a106a 100644 (file)
@@ -45,7 +45,8 @@ String FeatureEnableCommand::GetShortDescription(void) const
 }
 
 void FeatureEnableCommand::InitParameters(boost::program_options::options_description& visibleDesc,
-    boost::program_options::options_description& hiddenDesc) const
+    boost::program_options::options_description& hiddenDesc,
+    ArgumentCompletionDescription& argCompletionDesc) const
 {
         /* Command doesn't support any parameters. */
 }
index 982e9ce3ed3f1afadc6a9e6a79045f7649b75051..41f066660cd3bd3269af05f417ece0755778db9c 100644 (file)
@@ -39,7 +39,8 @@ public:
        virtual String GetDescription(void) const;
        virtual String GetShortDescription(void) const;
        virtual void InitParameters(boost::program_options::options_description& visibleDesc,
-           boost::program_options::options_description& hiddenDesc) const;
+           boost::program_options::options_description& hiddenDesc,
+           ArgumentCompletionDescription& argCompletionDesc) const;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
 
 };
index 9e2f59cd8abb0366c642290892e07d062046b99a..eaa66ced2a78be0032a933ab700668b3d19708f5 100644 (file)
@@ -45,7 +45,8 @@ String FeatureListCommand::GetShortDescription(void) const
 }
 
 void FeatureListCommand::InitParameters(boost::program_options::options_description& visibleDesc,
-    boost::program_options::options_description& hiddenDesc) const
+    boost::program_options::options_description& hiddenDesc,
+    ArgumentCompletionDescription& argCompletionDesc) const
 {
         /* Command doesn't support any parameters. */
 }
index 5ebdedc9208f187f4f5aa57a4e5ea8258089a73a..350bca2a6a6583891ce08f6b20cb02b98bdd880e 100644 (file)
@@ -39,7 +39,8 @@ public:
        virtual String GetDescription(void) const;
        virtual String GetShortDescription(void) const;
        virtual void InitParameters(boost::program_options::options_description& visibleDesc,
-           boost::program_options::options_description& hiddenDesc) const;
+           boost::program_options::options_description& hiddenDesc,
+           ArgumentCompletionDescription& argCompletionDesc) const;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
 
 private:
index 19b4aeed5d2627f53c80ae8e1d87cefa62000718..71ee57cb1bbded9d72b3f1cb66d1169e7495baa3 100644 (file)
@@ -39,7 +39,8 @@ String PKINewCACommand::GetShortDescription(void) const
 }
 
 void PKINewCACommand::InitParameters(boost::program_options::options_description& visibleDesc,
-    boost::program_options::options_description& hiddenDesc) const
+    boost::program_options::options_description& hiddenDesc,
+    ArgumentCompletionDescription& argCompletionDesc) const
 {
        /* Command doesn't support any parameters. */
 }
index b2434ddeab88400cacded58d4577f0da7c950af7..e063f70b8635754c5eaf8e4ff63ca5f53342f1a4 100644 (file)
@@ -39,7 +39,8 @@ public:
        virtual String GetDescription(void) const;
        virtual String GetShortDescription(void) const;
        virtual void InitParameters(boost::program_options::options_description& visibleDesc,
-           boost::program_options::options_description& hiddenDesc) const;
+           boost::program_options::options_description& hiddenDesc,
+           ArgumentCompletionDescription& argCompletionDesc) const;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
 
 };
index 4c36266f8ebbd82c2ff7e3200003d85f738f3abd..3c38cf2b8ff6ba9d2f23fcc8bf24c7ab94033ab3 100644 (file)
@@ -38,7 +38,8 @@ String PKINewCertCommand::GetShortDescription(void) const
 }
 
 void PKINewCertCommand::InitParameters(boost::program_options::options_description& visibleDesc,
-    boost::program_options::options_description& hiddenDesc) const
+    boost::program_options::options_description& hiddenDesc,
+    ArgumentCompletionDescription& argCompletionDesc) const
 {
        visibleDesc.add_options()
                ("cn", po::value<std::string>(), "Common Name")
index 051d085ce5faa92f8218b6cd714abe84b3518016..98f356a5e2e6a31e110074566b0e1f88f22421ab 100644 (file)
@@ -39,7 +39,8 @@ public:
        virtual String GetDescription(void) const;
        virtual String GetShortDescription(void) const;
        virtual void InitParameters(boost::program_options::options_description& visibleDesc,
-           boost::program_options::options_description& hiddenDesc) const;
+           boost::program_options::options_description& hiddenDesc,
+           ArgumentCompletionDescription& argCompletionDesc) const;
        virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
 
 };