Add support for Qt5's native command line parser
authorManuel Nickschas <manuel.nickschas@bmw-carit.de>
Sat, 31 Jan 2015 13:13:45 +0000 (14:13 +0100)
committerManuel Nickschas <manuel.nickschas@bmw-carit.de>
Sat, 31 Jan 2015 13:13:45 +0000 (14:13 +0100)
Qt 5.2 finally has grown a way for parsing command line arguments,
namely QCommandLineParser. According to the KF5 porting docs, this
is also the preferred choice for software using KDE Frameworks.

This commit adds a Qt5-based implementation for AbstractCliParser and
uses that instead of our own solution if Quassel is built against Qt5.
We also add support for naming the values that are shown in the --help
output, as QCommandLineParser can make use of that. Once we stop
supporting Qt4, the CLI parser abstraction shall be killed, simplifying
things a lot.

src/common/CMakeLists.txt
src/common/abstractcliparser.h
src/common/cliparser.cpp
src/common/main.cpp
src/common/qt5cliparser.cpp [new file with mode: 0644]
src/common/qt5cliparser.h [new file with mode: 0644]
src/uisupport/kcmdlinewrapper.cpp

index fa48020..3f08a2c 100644 (file)
@@ -9,7 +9,6 @@ set(SOURCES
     buffersyncer.cpp
     bufferviewconfig.cpp
     bufferviewmanager.cpp
     buffersyncer.cpp
     bufferviewconfig.cpp
     bufferviewmanager.cpp
-    cliparser.cpp
     compressor.cpp
     ctcpevent.cpp
     event.cpp
     compressor.cpp
     ctcpevent.cpp
     event.cpp
@@ -46,6 +45,11 @@ set(SOURCES
     coreinfo.h
 )
 
     coreinfo.h
 )
 
+if (USE_QT5)
+    list(APPEND SOURCES qt5cliparser.cpp)
+else()
+    list(APPEND SOURCES cliparser.cpp)
+endif()
 
 if (QCA2_FOUND)
     set(SOURCES ${SOURCES} keyevent.cpp)
 
 if (QCA2_FOUND)
     set(SOURCES ${SOURCES} keyevent.cpp)
index 2441a6e..293ccd9 100644 (file)
@@ -36,9 +36,9 @@ public:
     }
 
 
     }
 
 
-    inline void addOption(const QString &longName, const char shortName = 0, const QString &help = QString(), const QString &def = QString())
+    inline void addOption(const QString &longName, const char shortName = 0, const QString &help = QString(), const QString &valueName = QString(), const QString &def = QString())
     {
     {
-        addArgument(longName, CliParserArg(CliParserArg::CliArgOption, shortName, help, def));
+        addArgument(longName, CliParserArg(CliParserArg::CliArgOption, shortName, help, valueName, def));
     }
 
 
     }
 
 
@@ -54,20 +54,21 @@ protected:
             CliArgOption
         };
 
             CliArgOption
         };
 
-        CliParserArg(const CliArgType _type = CliArgInvalid, const char _shortName = 0, const QString _help = QString(), const QString _def = QString())
-            : type(_type),
-            shortName(_shortName),
-            help(_help),
-            def(_def),
-            value(QString()),
-            boolValue(false) {};
+        CliParserArg(const CliArgType type = CliArgInvalid, const char shortName = 0, const QString &help = QString(), const QString &valueName = QString(), const QString def = QString())
+            : type(type)
+            , shortName(shortName)
+            , help(help)
+            , valueName(valueName)
+            , def(def)
+            {};
 
         CliArgType type;
         char shortName;
         QString help;
 
         CliArgType type;
         char shortName;
         QString help;
+        QString valueName;
         QString def;
         QString value;
         QString def;
         QString value;
-        bool boolValue;
+        bool boolValue = false;
     };
 
     virtual void addArgument(const QString &longName, const CliParserArg &arg) = 0;
     };
 
     virtual void addArgument(const QString &longName, const CliParserArg &arg) = 0;
index ac70b4a..2e94e55 100644 (file)
@@ -32,10 +32,8 @@ CliParser::CliParser() : AbstractCliParser()
 }
 
 
 }
 
 
-void CliParser::addArgument(const QString &longName_, const CliParserArg &arg)
+void CliParser::addArgument(const QString &longName, const CliParserArg &arg)
 {
 {
-    QString longName = longName_;
-    longName.remove(QRegExp("\\s*<.*>\\s*")); // KCmdLineArgs takes args of the form "arg <defval>"
     if (argsMap.contains(longName)) qWarning() << "Warning: Multiple definition of argument" << longName;
     if (arg.shortName != 0 && !lnameOfShortArg(arg.shortName).isNull())
         qWarning().nospace() << "Warning: Redefining shortName '" << arg.shortName << "' for " << longName << " previously defined for " << lnameOfShortArg(arg.shortName);
     if (argsMap.contains(longName)) qWarning() << "Warning: Multiple definition of argument" << longName;
     if (arg.shortName != 0 && !lnameOfShortArg(arg.shortName).isNull())
         qWarning().nospace() << "Warning: Redefining shortName '" << arg.shortName << "' for " << longName << " previously defined for " << lnameOfShortArg(arg.shortName);
@@ -189,8 +187,8 @@ void CliParser::usage()
         }
         else output.append("    ");
         lnameField.append(" --").append(arg.key());
         }
         else output.append("    ");
         lnameField.append(" --").append(arg.key());
-        if (arg.value().type == CliParserArg::CliArgOption) {
-            lnameField.append("=[").append(arg.key().toUpper()).append("]");
+        if (arg.value().type == CliParserArg::CliArgOption && !arg.value().valueName.isEmpty()) {
+            lnameField.append("=<").append(arg.value().valueName).append(">");
         }
         output.append(lnameField.leftJustified(lnameFieldSize));
         if (!arg.value().help.isEmpty()) {
         }
         output.append(lnameField.leftJustified(lnameFieldSize));
         if (!arg.value().help.isEmpty()) {
index 6552598..48b45c2 100644 (file)
 #ifdef HAVE_KDE4
 #  include <KAboutData>
 #  include "kcmdlinewrapper.h"
 #ifdef HAVE_KDE4
 #  include <KAboutData>
 #  include "kcmdlinewrapper.h"
+#elif defined HAVE_QT5
+#  include "qt5cliparser.h"
+#else
+#  include "cliparser.h"
 #endif
 
 #if !defined(BUILD_CORE) && defined(STATIC)
 #endif
 
 #if !defined(BUILD_CORE) && defined(STATIC)
@@ -47,13 +51,13 @@ Q_IMPORT_PLUGIN(qjpeg)
 Q_IMPORT_PLUGIN(qgif)
 #endif
 
 Q_IMPORT_PLUGIN(qgif)
 #endif
 
-#include "cliparser.h"
 #include "quassel.h"
 
 int main(int argc, char **argv)
 {
     Quassel::setupBuildInfo();
     QCoreApplication::setApplicationName(Quassel::buildInfo().applicationName);
 #include "quassel.h"
 
 int main(int argc, char **argv)
 {
     Quassel::setupBuildInfo();
     QCoreApplication::setApplicationName(Quassel::buildInfo().applicationName);
+    QCoreApplication::setApplicationVersion(Quassel::buildInfo().plainVersionString);
     QCoreApplication::setOrganizationName(Quassel::buildInfo().organizationName);
     QCoreApplication::setOrganizationDomain(Quassel::buildInfo().organizationDomain);
 
     QCoreApplication::setOrganizationName(Quassel::buildInfo().organizationName);
     QCoreApplication::setOrganizationDomain(Quassel::buildInfo().organizationDomain);
 
@@ -95,6 +99,8 @@ int main(int argc, char **argv)
     KCmdLineArgs::init(argc, argv, &aboutData);
 
     cliParser = new KCmdLineWrapper();
     KCmdLineArgs::init(argc, argv, &aboutData);
 
     cliParser = new KCmdLineWrapper();
+#elif defined HAVE_QT5
+    cliParser = new Qt5CliParser();
 #else
     cliParser = new CliParser();
 #endif
 #else
     cliParser = new CliParser();
 #endif
@@ -108,35 +114,35 @@ int main(int argc, char **argv)
     cliParser->addSwitch("help", 'h', "Display this help and exit");
     cliParser->addSwitch("version", 'v', "Display version information");
 #ifdef BUILD_QTUI
     cliParser->addSwitch("help", 'h', "Display this help and exit");
     cliParser->addSwitch("version", 'v', "Display version information");
 #ifdef BUILD_QTUI
-    cliParser->addOption("configdir <path>", 'c', "Specify the directory holding the client configuration");
+    cliParser->addOption("configdir", 'c', "Specify the directory holding the client configuration", "path");
 #else
 #else
-    cliParser->addOption("configdir <path>", 'c', "Specify the directory holding configuration files, the SQlite database and the SSL certificate");
+    cliParser->addOption("configdir", 'c', "Specify the directory holding configuration files, the SQlite database and the SSL certificate", "path");
 #endif
 #endif
-    cliParser->addOption("datadir <path>", 0, "DEPRECATED - Use --configdir instead");
+    cliParser->addOption("datadir", 0, "DEPRECATED - Use --configdir instead", "path");
 
 #ifndef BUILD_CORE
     // put client-only arguments here
 
 #ifndef BUILD_CORE
     // put client-only arguments here
-    cliParser->addOption("icontheme <theme>", 0, "Override the system icon theme ('oxygen' is recommended)");
-    cliParser->addOption("qss <file.qss>", 0, "Load a custom application stylesheet");
+    cliParser->addOption("icontheme", 0, "Override the system icon theme ('oxygen' is recommended)", "theme");
+    cliParser->addOption("qss", 0, "Load a custom application stylesheet", "file.qss");
     cliParser->addSwitch("debugbufferswitches", 0, "Enables debugging for bufferswitches");
     cliParser->addSwitch("debugmodel", 0, "Enables debugging for models");
     cliParser->addSwitch("hidewindow", 0, "Start the client minimized to the system tray");
 #endif
 #ifndef BUILD_QTUI
     // put core-only arguments here
     cliParser->addSwitch("debugbufferswitches", 0, "Enables debugging for bufferswitches");
     cliParser->addSwitch("debugmodel", 0, "Enables debugging for models");
     cliParser->addSwitch("hidewindow", 0, "Start the client minimized to the system tray");
 #endif
 #ifndef BUILD_QTUI
     // put core-only arguments here
-    cliParser->addOption("listen <address>[,<address[,...]]>", 0, "The address(es) quasselcore will listen on", "::,0.0.0.0");
-    cliParser->addOption("port <port>", 'p', "The port quasselcore will listen at", QString("4242"));
+    cliParser->addOption("listen", 0, "The address(es) quasselcore will listen on", "<address>[,<address>[,...]]", "::,0.0.0.0");
+    cliParser->addOption("port", 'p', "The port quasselcore will listen at", "port", "4242");
     cliParser->addSwitch("norestore", 'n', "Don't restore last core's state");
     cliParser->addSwitch("norestore", 'n', "Don't restore last core's state");
-    cliParser->addOption("loglevel <level>", 'L', "Loglevel Debug|Info|Warning|Error", "Info");
+    cliParser->addOption("loglevel", 'L', "Loglevel Debug|Info|Warning|Error", "level", "Info");
 #ifdef HAVE_SYSLOG
     cliParser->addSwitch("syslog", 0, "Log to syslog");
 #endif
 #ifdef HAVE_SYSLOG
     cliParser->addSwitch("syslog", 0, "Log to syslog");
 #endif
-    cliParser->addOption("logfile <path>", 'l', "Log to a file");
-    cliParser->addOption("select-backend <backendidentifier>", 0, "Switch storage backend (migrating data if possible)");
+    cliParser->addOption("logfile", 'l', "Log to a file", "path");
+    cliParser->addOption("select-backend", 0, "Switch storage backend (migrating data if possible)", "backendidentifier");
     cliParser->addSwitch("add-user", 0, "Starts an interactive session to add a new core user");
     cliParser->addSwitch("add-user", 0, "Starts an interactive session to add a new core user");
-    cliParser->addOption("change-userpass <username>", 0, "Starts an interactive session to change the password of the user identified by username");
+    cliParser->addOption("change-userpass", 0, "Starts an interactive session to change the password of the user identified by <username>", "username");
     cliParser->addSwitch("oidentd", 0, "Enable oidentd integration");
     cliParser->addSwitch("oidentd", 0, "Enable oidentd integration");
-    cliParser->addOption("oidentd-conffile <file>", 0, "Set path to oidentd configuration file");
+    cliParser->addOption("oidentd-conffile", 0, "Set path to oidentd configuration file", "file");
 #ifdef HAVE_SSL
     cliParser->addSwitch("require-ssl", 0, "Require SSL for client connections");
 #endif
 #ifdef HAVE_SSL
     cliParser->addSwitch("require-ssl", 0, "Require SSL for client connections");
 #endif
diff --git a/src/common/qt5cliparser.cpp b/src/common/qt5cliparser.cpp
new file mode 100644 (file)
index 0000000..e224911
--- /dev/null
@@ -0,0 +1,72 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2015 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program 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 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program 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 this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#include "qt5cliparser.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+
+bool Qt5CliParser::init(const QStringList &arguments)
+{
+    _qCliParser.addHelpOption();
+    _qCliParser.addVersionOption();
+    _qCliParser.setApplicationDescription(QCoreApplication::translate("CliParser", "Quassel IRC is a modern, distributed IRC client."));
+
+    _qCliParser.process(arguments);
+    return true; // process() does error handling by itself
+}
+
+
+bool Qt5CliParser::isSet(const QString &longName)
+{
+
+    return _qCliParser.isSet(longName);
+}
+
+
+QString Qt5CliParser::value(const QString &longName)
+{
+    return _qCliParser.value(longName);
+}
+
+
+void Qt5CliParser::usage()
+{
+    _qCliParser.showHelp();
+}
+
+
+void Qt5CliParser::addArgument(const QString &longName, const AbstractCliParser::CliParserArg &arg)
+{
+    QStringList names(longName);
+    if (arg.shortName != 0)
+        names << QString(arg.shortName);
+
+    switch(arg.type) {
+    case CliParserArg::CliArgSwitch:
+        _qCliParser.addOption(QCommandLineOption(names, arg.help));
+        break;
+    case CliParserArg::CliArgOption:
+        _qCliParser.addOption(QCommandLineOption(names, arg.help, arg.valueName, arg.def));
+        break;
+    default:
+        qWarning() << "Warning: Unrecognized argument:" << longName;
+    }
+}
diff --git a/src/common/qt5cliparser.h b/src/common/qt5cliparser.h
new file mode 100644 (file)
index 0000000..1a7e788
--- /dev/null
@@ -0,0 +1,42 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2015 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program 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 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program 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 this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#pragma once
+
+#include <QCommandLineParser>
+
+#include "abstractcliparser.h"
+
+
+class Qt5CliParser : public AbstractCliParser
+{
+public:
+    bool init(const QStringList &arguments = QStringList());
+
+    QString value(const QString &longName);
+    bool isSet(const QString &longName);
+    void usage();
+
+private:
+    void addArgument(const QString &longName, const CliParserArg &arg);
+
+    QCommandLineParser _qCliParser;
+
+};
index fc102cc..a263e3c 100644 (file)
@@ -27,11 +27,16 @@ KCmdLineWrapper::KCmdLineWrapper()
 }
 
 
 }
 
 
-void KCmdLineWrapper::addArgument(const QString &longName, const CliParserArg &arg)
+void KCmdLineWrapper::addArgument(const QString &longName_, const CliParserArg &arg)
 {
 {
+    QString longName = longName_;
+    if (arg.type == CliParserArg::CliArgOption && !arg.valueName.isEmpty())
+        longName += " <" + arg.valueName + ">";
+
     if (arg.shortName != 0) {
     if (arg.shortName != 0) {
-        _cmdLineOptions.add(QByteArray().append(arg.shortName));
+        _cmdLineOptions.add(QByteArray(1, arg.shortName));
     }
     }
+
     _cmdLineOptions.add(longName.toUtf8(), ki18n(arg.help.toUtf8()), arg.def.toUtf8());
 }
 
     _cmdLineOptions.add(longName.toUtf8(), ki18n(arg.help.toUtf8()), arg.def.toUtf8());
 }