File indexing completed on 2024-04-14 14:19:57

0001 /*
0002    Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License version 2 as published by the Free Software Foundation.
0007 
0008    This library is distributed in the hope that it will be useful,
0009    but WITHOUT ANY WARRANTY; without even the implied warranty of
0010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0011    Library General Public License for more details.
0012 
0013    You should have received a copy of the GNU Library General Public License
0014    along with this library; see the file COPYING.LIB.  If not, write to
0015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0016    Boston, MA 02110-1301, USA.
0017 */
0018 
0019 #include "kcmdlineargs.h"
0020 
0021 #include <config-kdelibs4support.h>
0022 
0023 #include <sys/param.h>
0024 
0025 #include <assert.h>
0026 #include <stdio.h>
0027 #include <stdlib.h>
0028 #include <string.h>
0029 #include <unistd.h>
0030 #include <locale.h>
0031 
0032 #if HAVE_LIMITS_H
0033 #include <limits.h>
0034 #endif
0035 
0036 #include <QDir>
0037 #include <QDebug>
0038 #include <QFile>
0039 #include <QHash>
0040 #include <QTextCodec>
0041 #include <QUrl>
0042 #include <QDataStream>
0043 
0044 #include "k4aboutdata.h"
0045 #include <kcoreaddons_version.h>
0046 
0047 // -----------------------------------------------------------------------------
0048 // Design notes:
0049 //
0050 // These classes deal with a lot of text, some of which needs to be
0051 // marked for translation. Since at the time when these object and calls are
0052 // made the translation catalogs are usually still not initialized, the
0053 // translation has to be delayed. This is achieved by using KLocalizedString
0054 // for translatable strings. KLocalizedStrings are produced by ki18n* calls,
0055 // instead of the more usuall i18n* calls which produce QString by trying to
0056 // translate immediately.
0057 //
0058 // All the non-translatable string arguments to methods are taken QByteArray,
0059 // all the translatable are KLocalizedString. The getter methods always return
0060 // proper QString: the non-translatable strings supplied by the code are
0061 // treated with QString::fromUtf8(), those coming from the outside with
0062 // QTextCodec::toUnicode(), and translatable strings are finalized to QStrings
0063 // at the point of getter calls (i.e. delayed translation).
0064 //
0065 // The code below uses locally defined s->decodeInput(QByteArray) and
0066 // s->encodeOutput(QString) calls to centralize the conversion of raw external
0067 // bytes (instead of QString::to/fromLocal8Bit(), QFile::decodeName, etc.)
0068 // -----------------------------------------------------------------------------
0069 
0070 #if HAVE_X11
0071 #define DISPLAY "DISPLAY"
0072 #else
0073 #define DISPLAY "NODISPLAY"
0074 #endif
0075 
0076 //
0077 // Helper classes
0078 //
0079 
0080 class KCmdLineParsedOptions : public QHash<QByteArray, QByteArray>
0081 {
0082 public:
0083     KCmdLineParsedOptions() { }
0084 };
0085 
0086 class KCmdLineParsedArgs : public QList<QByteArray>
0087 {
0088 public:
0089     KCmdLineParsedArgs() { }
0090 };
0091 
0092 class KCmdLineArgsList: public QList<KCmdLineArgs *>
0093 {
0094 public:
0095     KCmdLineArgsList() { }
0096     ~KCmdLineArgsList()
0097     {
0098         while (count()) {
0099             delete takeFirst();
0100         }
0101     }
0102 };
0103 
0104 //
0105 // KCmdLineOptions
0106 //
0107 
0108 class KCmdLineOptionsPrivate
0109 {
0110 public:
0111     QList<QByteArray> names;
0112     QList<KLocalizedString> descriptions;
0113     QStringList defaults;
0114 };
0115 
0116 KCmdLineOptions::KCmdLineOptions()
0117     : d(new KCmdLineOptionsPrivate)
0118 {}
0119 
0120 KCmdLineOptions::~KCmdLineOptions()
0121 {
0122     delete d;
0123 }
0124 
0125 KCmdLineOptions::KCmdLineOptions(const KCmdLineOptions &options)
0126     : d(new KCmdLineOptionsPrivate(*(options.d)))
0127 {
0128 }
0129 
0130 KCmdLineOptions &KCmdLineOptions::operator= (const KCmdLineOptions &options)
0131 {
0132     if (this != &options) {
0133         *d = *(options.d);
0134     }
0135     return *this;
0136 }
0137 
0138 KCmdLineOptions &KCmdLineOptions::add(const QByteArray &name,
0139                                       const KLocalizedString &description,
0140                                       const QByteArray &defaultValue)
0141 {
0142     d->names.append(name);
0143     d->descriptions.append(description);
0144     d->defaults.append(QString::fromUtf8(defaultValue.data()));
0145     return *this;
0146 }
0147 
0148 KCmdLineOptions &KCmdLineOptions::add(const KCmdLineOptions &other)
0149 {
0150     d->names += other.d->names;
0151     d->descriptions += other.d->descriptions;
0152     d->defaults += other.d->defaults;
0153     return *this;
0154 }
0155 
0156 //
0157 // KCmdLineArgs static data and methods
0158 //
0159 
0160 class KCmdLineArgsStatic
0161 {
0162 public:
0163 
0164     KCmdLineArgsList *argsList; // All options.
0165     const K4AboutData *about;
0166 
0167     int all_argc; // The original argc
0168     char **all_argv; // The original argv
0169     char *appName;
0170     bool parsed : 1; // Whether we have parsed the arguments since calling init
0171     bool ignoreUnknown : 1; // Ignore unknown options and arguments
0172     QByteArray mCwd; // Current working directory. Important for KUnqiueApp!
0173     KCmdLineArgs::StdCmdLineArgs mStdargs;
0174 
0175     KCmdLineOptions qt_options;
0176     KCmdLineOptions kde_options;
0177 
0178     KCmdLineArgsStatic();
0179 
0180     ~KCmdLineArgsStatic();
0181 
0182     QTextCodec *codec; // codec for converting raw input to QString
0183 
0184     /**
0185      * @internal
0186      * Convertes raw command line argument data to proper QString.
0187      *
0188      * @param rawstr raw text
0189      * @return properly decoded QString
0190      */
0191     static QString decodeInput(const QByteArray &rawstr);
0192 
0193     /**
0194      * @internal
0195      * Convertes QString to raw command line output.
0196      *
0197      * @param str string to be encoded
0198      * @return raw text
0199      */
0200     static QByteArray encodeOutput(const QString &str);
0201 
0202     /**
0203      * @internal
0204      * Shell output with proper decoding.
0205      */
0206     void printQ(const QString &msg);
0207 
0208     /**
0209      * @internal
0210      * Try to match given option in the list of options.
0211      * Returns match status.
0212      *
0213      * @return:
0214      *  0 - option not found.
0215      *  1 - option found      // -fork
0216      *  2 - inverse option found ('no') // -nofork
0217      *  3 - option + arg found    // -fork now
0218      *
0219      *  +4 - no more options follow         // !fork
0220      */
0221     static int findOption(const KCmdLineOptions &options, QByteArray &opt,
0222                           QByteArray &opt_name, QString &def, bool &enabled);
0223 
0224     /**
0225      * @internal
0226      *
0227      * Checks what to do with a single option
0228      */
0229     static void findOption(const QByteArray &optv, const QByteArray &_opt,
0230                            int &i, bool _enabled, bool &moreOptions);
0231 
0232     /**
0233      * @internal
0234      *
0235      * Parse all arguments, verify correct syntax and put all arguments
0236      * where they belong.
0237      */
0238     static void parseAllArgs();
0239 
0240     /**
0241      * @internal
0242      *
0243      * Remove named options.
0244      *
0245      * @param id The name of the options to be removed.
0246      */
0247     static void removeArgs(const QByteArray &id);
0248 
0249     /**
0250      * @internal
0251      *
0252      * Convert &, ", ', <, > characters into XML entities
0253      * &amp;, &lt;, &gt;, &apos;, &quot;, respectively.
0254      */
0255     static QString escape(const QString &text);
0256 };
0257 Q_GLOBAL_STATIC(KCmdLineArgsStatic, staticObj)
0258 
0259 KCmdLineArgsStatic::KCmdLineArgsStatic()
0260 {
0261     // Global data
0262     argsList = nullptr;
0263     all_argc = 0;
0264     all_argv = nullptr;
0265     appName = nullptr;
0266     mCwd.clear();
0267     about = nullptr;
0268     parsed = false;
0269     ignoreUnknown = false;
0270     mStdargs = {};
0271 
0272     // Text codec.
0273     codec = QTextCodec::codecForLocale();
0274 
0275     // Qt options
0276     //FIXME: Check if other options are specific to Qt/X11
0277 #if HAVE_X11
0278     qt_options.add("display <displayname>", ki18n("Use the X-server display 'displayname'"));
0279 #else
0280 #endif
0281     qt_options.add("session <sessionId>", ki18n("Restore the application for the given 'sessionId'"));
0282     qt_options.add("cmap", ki18n("Causes the application to install a private color\nmap on an 8-bit display"));
0283     qt_options.add("ncols <count>", ki18n("Limits the number of colors allocated in the color\ncube on an 8-bit display, if the application is\nusing the QApplication::ManyColor color\nspecification"));
0284     qt_options.add("nograb", ki18n("tells Qt to never grab the mouse or the keyboard"));
0285     qt_options.add("dograb", ki18n("running under a debugger can cause an implicit\n-nograb, use -dograb to override"));
0286     qt_options.add("sync", ki18n("switches to synchronous mode for debugging"));
0287     qt_options.add("fn");
0288     qt_options.add("font <fontname>", ki18n("defines the application font"));
0289     qt_options.add("bg");
0290     qt_options.add("background <color>", ki18n("sets the default background color and an\napplication palette (light and dark shades are\ncalculated)"));
0291     qt_options.add("fg");
0292     qt_options.add("foreground <color>", ki18n("sets the default foreground color"));
0293     qt_options.add("btn");
0294     qt_options.add("button <color>", ki18n("sets the default button color"));
0295     qt_options.add("name <name>", ki18n("sets the application name"));
0296     qt_options.add("title <title>", ki18n("sets the application title (caption)"));
0297     qt_options.add("testability", ki18n("load the testability framework"));
0298 #if HAVE_X11
0299     qt_options.add("visual TrueColor", ki18n("forces the application to use a TrueColor visual on\nan 8-bit display"));
0300     qt_options.add("inputstyle <inputstyle>", ki18n("sets XIM (X Input Method) input style. Possible\nvalues are onthespot, overthespot, offthespot and\nroot"));
0301     qt_options.add("im <XIM server>", ki18n("set XIM server"));
0302     qt_options.add("noxim", ki18n("disable XIM"));
0303 #endif
0304     qt_options.add("reverse", ki18n("mirrors the whole layout of widgets"));
0305     qt_options.add("stylesheet <file.qss>", ki18n("applies the Qt stylesheet to the application widgets"));
0306     qt_options.add("graphicssystem <system>", ki18n("use a different graphics system instead of the default one, options are raster and opengl (experimental)"));
0307     qt_options.add("qmljsdebugger <port>", ki18n("QML JS debugger information. Application must be\nbuilt with -DQT_DECLARATIVE_DEBUG for the debugger to be\nenabled"));
0308     qt_options.add("platform <platform>", ki18n("The windowing system platform (e.g. xcb or wayland)"));
0309     // KDE options
0310     kde_options.add("caption <caption>",   ki18n("Use 'caption' as name in the titlebar"));
0311     kde_options.add("icon <icon>",         ki18n("Use 'icon' as the application icon"));
0312     kde_options.add("config <filename>",   ki18n("Use alternative configuration file"));
0313     kde_options.add("nocrashhandler",      ki18n("Disable crash handler, to get core dumps"));
0314 #if HAVE_X11
0315     kde_options.add("waitforwm",           ki18n("Waits for a WM_NET compatible windowmanager"));
0316 #endif
0317     kde_options.add("style <style>",       ki18n("sets the application GUI style"));
0318     kde_options.add("geometry <geometry>", ki18n("sets the client geometry of the main widget - see man X for the argument format (usually WidthxHeight+XPos+YPos)"));
0319 #ifndef Q_OS_WIN
0320     kde_options.add("smkey <sessionKey>"); // this option is obsolete and exists only to allow smooth upgrades from sessions
0321 #endif
0322 }
0323 
0324 KCmdLineArgsStatic::~KCmdLineArgsStatic()
0325 {
0326     delete argsList;
0327     // K4AboutData object is deleted by ~KCleanUpGlobalStatic.
0328     //delete about;
0329 }
0330 
0331 //
0332 // KCmdLineArgs private data and methods
0333 //
0334 
0335 class KCmdLineArgsPrivate
0336 {
0337     friend class KCmdLineArgsStatic;
0338 public:
0339     KCmdLineArgsPrivate(const KCmdLineOptions &_options, const KLocalizedString &_name, const QByteArray &_id)
0340         : options(_options)
0341         , name(_name)
0342         , id(_id)
0343         , parsedOptionList(nullptr)
0344         , parsedArgList(nullptr)
0345         , isQt(id == "qt")
0346     {
0347     }
0348     ~KCmdLineArgsPrivate()
0349     {
0350         delete parsedOptionList;
0351         delete parsedArgList;
0352     }
0353     const KCmdLineOptions options;
0354     const KLocalizedString name;
0355     const QByteArray id;
0356     KCmdLineParsedOptions *parsedOptionList;
0357     KCmdLineParsedArgs *parsedArgList;
0358     bool isQt;
0359 
0360     /**
0361      * @internal
0362      *
0363      *  Set a boolean option
0364      */
0365     void setOption(const QByteArray &option, bool enabled);
0366 
0367     /**
0368      * @internal
0369      *
0370      *  Set a string option
0371      */
0372     void setOption(const QByteArray &option, const QByteArray &value);
0373 
0374     /**
0375      * @internal
0376      *
0377      * Add an argument
0378      */
0379     void addArgument(const QByteArray &argument);
0380 
0381     /**
0382      * @internal
0383      *
0384      * Save to a stream.
0385      */
0386     void save(QDataStream &) const;
0387 
0388     /**
0389      * @internal
0390      *
0391      * Restore from a stream.
0392      */
0393     void load(QDataStream &);
0394 };
0395 
0396 //
0397 // Static functions
0398 //
0399 
0400 QString
0401 KCmdLineArgsStatic::decodeInput(const QByteArray &rawstr)
0402 {
0403     return staticObj()->codec->toUnicode(rawstr);
0404 }
0405 
0406 QByteArray
0407 KCmdLineArgsStatic::encodeOutput(const QString &str)
0408 {
0409     return staticObj()->codec->fromUnicode(str);
0410 }
0411 
0412 void
0413 KCmdLineArgsStatic::printQ(const QString &msg)
0414 {
0415     fprintf(stdout, "%s", encodeOutput(msg).data());
0416 }
0417 
0418 void
0419 KCmdLineArgs::init(int _argc, char **_argv,
0420                    const QByteArray &_appname,
0421                    const QByteArray &_catalog,
0422                    const KLocalizedString &_programName,
0423                    const QByteArray &_version,
0424                    const KLocalizedString &_description,
0425                    StdCmdLineArgs stdargs)
0426 {
0427     init(_argc, _argv,
0428          new K4AboutData(_appname, _catalog, _programName, _version, _description),
0429          stdargs);
0430 }
0431 
0432 void
0433 KCmdLineArgs::initIgnore(int _argc, char **_argv, const QByteArray &_appname)
0434 {
0435     init(_argc, _argv,
0436          new K4AboutData(_appname, nullptr, ki18n(_appname.data()), "unknown", ki18n("KDE Application")));
0437     staticObj()->ignoreUnknown = true;
0438 }
0439 
0440 void
0441 KCmdLineArgs::init(const K4AboutData *ab)
0442 {
0443     char **_argv = (char **) malloc(sizeof(char *));
0444     _argv[0] = (char *) staticObj()->encodeOutput(ab->appName()).data();
0445     init(1, _argv, ab, CmdLineArgNone);
0446 }
0447 
0448 void
0449 KCmdLineArgs::init(int _argc, char **_argv, const K4AboutData *_about, StdCmdLineArgs stdargs)
0450 {
0451     staticObj()->all_argc = _argc;
0452     staticObj()->all_argv = _argv;
0453 
0454     if (!staticObj()->all_argv) {
0455         fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
0456         fprintf(stderr, "Passing null-pointer to 'argv' is not allowed.\n\n");
0457 
0458         assert(0);
0459         exit(255);
0460     }
0461 
0462     // Strip path from argv[0]
0463     if (staticObj()->all_argc) {
0464         char *p = strrchr(staticObj()->all_argv[0], QDir::separator().toLatin1());
0465         if (p) {
0466             staticObj()->appName = p + 1;
0467         } else {
0468             staticObj()->appName = staticObj()->all_argv[0];
0469         }
0470     }
0471 
0472     staticObj()->about = _about;
0473     staticObj()->parsed = false;
0474     staticObj()->mCwd = QDir::currentPath().toLocal8Bit(); //currentPath() uses fromLocal8Bit internally apparently
0475     addStdCmdLineOptions(stdargs);
0476 }
0477 
0478 QString KCmdLineArgs::cwd()
0479 {
0480     return QString::fromLocal8Bit(staticObj()->mCwd.data());
0481 }
0482 
0483 QString KCmdLineArgs::appName()
0484 {
0485     if (!staticObj()->appName) {
0486         return QString();
0487     }
0488     return staticObj()->decodeInput(staticObj()->appName);
0489 }
0490 
0491 /**
0492   * Add Qt and KDE command line options to KCmdLineArgs.
0493   */
0494 void KCmdLineArgs::addStdCmdLineOptions(StdCmdLineArgs stdargs)
0495 {
0496     if (stdargs & KCmdLineArgs::CmdLineArgQt) {
0497         KCmdLineArgs::addCmdLineOptions(staticObj()->qt_options, ki18n("Qt"), "qt");
0498     }
0499     if (stdargs & KCmdLineArgs::CmdLineArgKDE) {
0500         KCmdLineArgs::addCmdLineOptions(staticObj()->kde_options, ki18n("KDE"), "kde");
0501     }
0502     staticObj()->mStdargs = stdargs;
0503 }
0504 
0505 void
0506 KCmdLineArgs::addCmdLineOptions(const KCmdLineOptions &options, const KLocalizedString &name,
0507                                 const QByteArray &id, const QByteArray &afterId)
0508 {
0509     if (!staticObj()->argsList) {
0510         staticObj()->argsList = new KCmdLineArgsList;
0511     }
0512 
0513     int pos = staticObj()->argsList->count();
0514     // To make sure that the named options come before unnamed.
0515     if (pos > 0 && !id.isEmpty() && staticObj()->argsList->last()->d->name.isEmpty()) {
0516         pos--;
0517     }
0518 
0519     KCmdLineArgsList::Iterator args;
0520     int i = 0;
0521     for (args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args, i++) {
0522         if (id == (*args)->d->id) {
0523             return; // Options already present.
0524         }
0525 
0526         // Only check for afterId if it has been given non-empty, as the
0527         // unnamed option group should come after all named groups.
0528         if (!afterId.isEmpty() && afterId == (*args)->d->id) {
0529             pos = i + 1;
0530         }
0531     }
0532 
0533     Q_ASSERT(staticObj()->parsed == false);   // You must add _ALL_ cmd line options
0534     // before accessing the arguments!
0535     staticObj()->argsList->insert(pos, new KCmdLineArgs(options, name, id));
0536 }
0537 
0538 void
0539 KCmdLineArgs::saveAppArgs(QDataStream &ds)
0540 {
0541     if (!staticObj()->parsed) {
0542         staticObj()->parseAllArgs();
0543     }
0544 
0545     // Remove Qt and KDE options.
0546     staticObj()->removeArgs("qt");
0547     staticObj()->removeArgs("kde");
0548     staticObj()->removeArgs("kuniqueapp");
0549 
0550     ds << staticObj()->mCwd;
0551 
0552     uint count = staticObj()->argsList ? staticObj()->argsList->count() : 0;
0553     ds << count;
0554 
0555     if (!count) {
0556         return;
0557     }
0558 
0559     KCmdLineArgsList::Iterator args;
0560     for (args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args) {
0561         ds << (*args)->d->id;
0562         (*args)->d->save(ds);
0563     }
0564 }
0565 
0566 void
0567 KCmdLineArgs::loadAppArgs(QDataStream &ds)
0568 {
0569     staticObj()->parsed = true; // don't reparse argc/argv!
0570 
0571     // Remove Qt and KDE options.
0572     staticObj()->removeArgs("qt");
0573     staticObj()->removeArgs("kde");
0574     staticObj()->removeArgs("kuniqueapp");
0575 
0576     KCmdLineArgsList::Iterator args;
0577     if (staticObj()->argsList) {
0578         for (args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args) {
0579             (*args)->clear();
0580         }
0581     }
0582 
0583     if (ds.atEnd()) {
0584         return;
0585     }
0586 
0587     QByteArray qCwd;
0588     ds >> qCwd;
0589 
0590     staticObj()->mCwd = qCwd;
0591 
0592     uint count;
0593     ds >> count;
0594 
0595     while (count--) {
0596         QByteArray id;
0597         ds >> id;
0598         Q_ASSERT(staticObj()->argsList);
0599         bool found = false;
0600         for (args = staticObj()->argsList->begin(); args != staticObj()->argsList->end(); ++args) {
0601             if ((*args)->d->id  == id) {
0602                 (*args)->d->load(ds);
0603                 found = true;
0604                 break;
0605             }
0606         }
0607         if (!found) {
0608             qWarning() << "Argument definitions for" << id << "not found!";
0609             // The next ds >> id will do nonsensical things...
0610         }
0611     }
0612     staticObj()->parsed = true;
0613 }
0614 
0615 KCmdLineArgs *KCmdLineArgs::parsedArgs(const QByteArray &id)
0616 {
0617     if (!staticObj()->argsList) {
0618         return nullptr;
0619     }
0620     KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
0621     while (args != staticObj()->argsList->end()) {
0622         if ((*args)->d->id == id) {
0623             if (!staticObj()->parsed) {
0624                 staticObj()->parseAllArgs();
0625             }
0626             return *args;
0627         }
0628         ++args;
0629     }
0630 
0631     return nullptr;
0632 }
0633 
0634 void KCmdLineArgsStatic::removeArgs(const QByteArray &id)
0635 {
0636     if (!staticObj()->argsList) {
0637         return;
0638     }
0639     KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
0640     while (args != staticObj()->argsList->end()) {
0641         if ((*args)->d->id == id) {
0642             if (!staticObj()->parsed) {
0643                 staticObj()->parseAllArgs();
0644             }
0645             break;
0646         }
0647         ++args;
0648     }
0649 
0650     if (args != staticObj()->argsList->end()) {
0651         KCmdLineArgs *a = *args;
0652         staticObj()->argsList->erase(args);
0653         delete a;
0654     }
0655 }
0656 
0657 #pragma message("KDE5 TODO: Remove this method once it is in Qt5")
0658 QString KCmdLineArgsStatic::escape(const QString &text)
0659 {
0660     int tlen = text.length();
0661     QString ntext;
0662     ntext.reserve(tlen);
0663     for (int i = 0; i < tlen; ++i) {
0664         QChar c = text[i];
0665         if (c == QLatin1Char('&')) {
0666             ntext += QLatin1String("&amp;");
0667         } else if (c == QLatin1Char('<')) {
0668             ntext += QLatin1String("&lt;");
0669         } else if (c == QLatin1Char('>')) {
0670             ntext += QLatin1String("&gt;");
0671         } else if (c == QLatin1Char('\'')) {
0672             ntext += QLatin1String("&apos;"); // not handled by Qt::escape
0673         } else if (c == QLatin1Char('"')) {
0674             ntext += QLatin1String("&quot;");
0675         } else {
0676             ntext += c;
0677         }
0678     }
0679     return ntext;
0680 }
0681 
0682 int
0683 KCmdLineArgsStatic::findOption(const KCmdLineOptions &options, QByteArray &opt,
0684                                QByteArray &opt_name, QString &def, bool &enabled)
0685 {
0686     int result;
0687     bool inverse;
0688 
0689     for (int i = 0; i < options.d->names.size(); i++) {
0690         result = 0;
0691         inverse = false;
0692         opt_name = options.d->names[i];
0693         if (opt_name.startsWith(':') || opt_name.isEmpty()) {
0694             continue;
0695         }
0696         if (opt_name.startsWith('!')) {
0697             opt_name = opt_name.mid(1);
0698             result = 4;
0699         }
0700         if (opt_name.startsWith("no") && !opt_name.contains('<')) { // krazy:exclude=strings
0701             opt_name = opt_name.mid(2);
0702             inverse = true;
0703         }
0704 
0705         int len = opt.length();
0706         if (opt == opt_name.left(len)) {
0707             opt_name = opt_name.mid(len);
0708             if (opt_name.isEmpty()) {
0709                 if (inverse) {
0710                     return result + 2;
0711                 }
0712 
0713                 if (options.d->descriptions[i].isEmpty()) {
0714                     i++;
0715                     if (i >= options.d->names.size()) {
0716                         return result + 0;
0717                     }
0718                     QByteArray nextOption = options.d->names[i];
0719                     int p = nextOption.indexOf(' ');
0720                     if (p > 0) {
0721                         nextOption = nextOption.left(p);
0722                     }
0723                     if (nextOption.startsWith('!')) {
0724                         nextOption = nextOption.mid(1);
0725                     }
0726                     if (nextOption.startsWith("no") && !nextOption.contains('<')) { // krazy:exclude=strings
0727                         nextOption = nextOption.mid(2);
0728                         enabled = !enabled;
0729                     }
0730                     result = findOption(options, nextOption, opt_name, def, enabled);
0731                     Q_ASSERT(result);
0732                     opt = nextOption;
0733                     return result;
0734                 }
0735 
0736                 return 1;
0737             }
0738             if (opt_name.startsWith(' ')) {
0739                 opt_name = opt_name.mid(1);
0740                 def = options.d->defaults[i];
0741                 return result + 3;
0742             }
0743         }
0744     }
0745     return 0;
0746 }
0747 
0748 void
0749 KCmdLineArgsStatic::findOption(const QByteArray &optv, const QByteArray &_opt,
0750                                int &i, bool _enabled, bool &moreOptions)
0751 {
0752     KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
0753     QByteArray opt = _opt;
0754     QByteArray opt_name;
0755     QString def;
0756     QByteArray argument;
0757     int j = opt.indexOf('=');
0758     if (j != -1) {
0759         argument = opt.mid(j + 1);
0760         opt = opt.left(j);
0761     }
0762 
0763     bool enabled = true;
0764     int result = 0;
0765     while (args != staticObj()->argsList->end()) {
0766         enabled = _enabled;
0767         result = findOption((*args)->d->options, opt, opt_name, def, enabled);
0768         if (result) {
0769             break;
0770         }
0771         ++args;
0772     }
0773     if ((args == staticObj()->argsList->end()) &&
0774             (optv.startsWith('-') && !optv.startsWith("--"))) { // krazy:exclude=strings
0775         // Option not found check if it is a valid option
0776         // in the style of -Pprinter1 or ps -aux
0777         int p = 1;
0778         while (true) {
0779             QByteArray singleCharOption = " "; // krazy:exclude=doublequote_chars
0780             singleCharOption[0] = optv[p];
0781             args = staticObj()->argsList->begin();
0782             while (args != staticObj()->argsList->end()) {
0783                 enabled = _enabled;
0784                 result = findOption((*args)->d->options, singleCharOption,
0785                                     opt_name, def, enabled);
0786                 if (result) {
0787                     break;
0788                 }
0789                 ++args;
0790             }
0791             if (args == staticObj()->argsList->end()) {
0792                 break;    // Unknown argument
0793             }
0794 
0795             p++;
0796             if (result == 1) { // Single option
0797                 (*args)->d->setOption(singleCharOption, enabled);
0798                 if (p < optv.length()) {
0799                     continue;    // Next option
0800                 } else {
0801                     return;    // Finished
0802                 }
0803             } else if (result == 3) { // This option takes an argument
0804                 if (argument.isEmpty()) {
0805                     argument = optv.mid(p);
0806                 }
0807                 (*args)->d->setOption(singleCharOption, argument);
0808                 return;
0809             }
0810             break; // Unknown argument
0811         }
0812         args = staticObj()->argsList->end();
0813         result = 0;
0814     }
0815 
0816     if (args == staticObj()->argsList->end() || !result) {
0817         if (staticObj()->ignoreUnknown) {
0818             return;
0819         }
0820         KCmdLineArgs::enable_i18n();
0821         KCmdLineArgs::usageError(i18n("Unknown option '%1'.", QString::fromLocal8Bit(_opt.data())));
0822     }
0823 
0824     if ((result & 4) != 0) {
0825         result &= ~4;
0826         moreOptions = false;
0827     }
0828 
0829     if (result == 3) { // This option takes an argument
0830         if (!enabled) {
0831             if (staticObj()->ignoreUnknown) {
0832                 return;
0833             }
0834             KCmdLineArgs::enable_i18n();
0835             KCmdLineArgs::usageError(i18n("Unknown option '%1'.", QString::fromLocal8Bit(_opt.data())));
0836         }
0837         if (argument.isEmpty()) {
0838             i++;
0839             if (i >= staticObj()->all_argc) {
0840                 KCmdLineArgs::enable_i18n();
0841                 KCmdLineArgs::usageError(i18nc("@info:shell %1 is cmdoption name", "'%1' missing.", QString::fromLocal8Bit(opt_name.data())));
0842             }
0843             argument = staticObj()->all_argv[i];
0844         }
0845         (*args)->d->setOption(opt, argument);
0846     } else {
0847         (*args)->d->setOption(opt, enabled);
0848     }
0849 }
0850 
0851 void
0852 KCmdLineArgsStatic::parseAllArgs()
0853 {
0854     bool allowArgs = false;
0855     bool inOptions = true;
0856     bool everythingAfterArgIsArgs = false;
0857     KCmdLineArgs *appOptions = staticObj()->argsList->last();
0858     if (appOptions->d->id.isEmpty()) {
0859         Q_FOREACH (const QByteArray &name, appOptions->d->options.d->names) {
0860             everythingAfterArgIsArgs = everythingAfterArgIsArgs || name.startsWith("!+");
0861             allowArgs = allowArgs || name.startsWith('+') || everythingAfterArgIsArgs;
0862         }
0863     }
0864     for (int i = 1; i < staticObj()->all_argc; i++) {
0865         if (!staticObj()->all_argv[i]) {
0866             continue;
0867         }
0868 
0869         if ((staticObj()->all_argv[i][0] == '-') && staticObj()->all_argv[i][1] && inOptions) {
0870             bool enabled = true;
0871             QByteArray orig = staticObj()->all_argv[i];
0872             QByteArray option = orig.mid(1);
0873             if (option.startsWith('-')) {
0874                 option = option.mid(1);
0875                 if (option.isEmpty()) {
0876                     inOptions = false;
0877                     continue;
0878                 }
0879             }
0880             if (option == "help") {
0881                 KCmdLineArgs::usage();
0882             } else if (option.startsWith("help-")) { // krazy:exclude=strings
0883                 KCmdLineArgs::usage(option.mid(5));
0884             }
0885 #ifdef Q_OS_MAC
0886             // skip the finder -psn_* hint
0887             else if (option.startsWith("psn_")) { // krazy:exclude=strings
0888                 continue;
0889             }
0890 #endif
0891             else if ((option == "version") || (option == "v")) {
0892                 KCmdLineArgs::enable_i18n();
0893                 staticObj()->printQ(i18nc("@info:shell message on appcmd --version; do not translate 'Development Platform'"
0894                                           "%3 application name, other %n version strings",
0895                                           "Qt: %1\n"
0896                                           "KDE Frameworks: %2\n"
0897                                           "%3: %4\n",
0898                                           QString::fromLatin1(qVersion()),
0899                                           QString::fromLatin1(KCOREADDONS_VERSION_STRING),
0900                                           staticObj()->about->programName(),
0901                                           staticObj()->about->version()));
0902                 exit(0);
0903             } else if (option == "license") {
0904                 KCmdLineArgs::enable_i18n();
0905                 staticObj()->printQ(staticObj()->about->license());
0906                 staticObj()->printQ(QString::fromLatin1("\n"));
0907                 exit(0);
0908             } else if (option == "author") {
0909                 KCmdLineArgs::enable_i18n();
0910                 if (staticObj()->about) {
0911                     const QList<K4AboutPerson> authors = staticObj()->about->authors();
0912                     if (!authors.isEmpty()) {
0913                         QString authorlist;
0914                         for (QList<K4AboutPerson>::ConstIterator it = authors.begin(); it != authors.end(); ++it) {
0915                             QString email;
0916                             if (!(*it).emailAddress().isEmpty()) {
0917                                 email = QString::fromLatin1(" &lt;") + (*it).emailAddress() + QLatin1String("&gt;");
0918                             }
0919                             authorlist += QString::fromLatin1("    ") + (*it).name() + email + QLatin1Char('\n');
0920                         }
0921                         staticObj()->printQ(i18nc("the 2nd argument is a list of name+address, one on each line", "%1 was written by\n%2", QString(staticObj()->about->programName()), authorlist));
0922                     }
0923                 } else {
0924                     staticObj()->printQ(i18n("This application was written by somebody who wants to remain anonymous."));
0925                 }
0926                 if (staticObj()->about) {
0927                     if (!staticObj()->about->customAuthorTextEnabled()) {
0928                         if (staticObj()->about->bugAddress().isEmpty() || staticObj()->about->bugAddress() == QLatin1String("submit@bugs.kde.org")) {
0929                             staticObj()->printQ(i18n("Please use http://bugs.kde.org to report bugs.\n"));
0930                         } else {
0931                             staticObj()->printQ(i18n("Please report bugs to %1.\n", staticObj()->about->bugAddress()));
0932                         }
0933                     } else {
0934                         staticObj()->printQ(staticObj()->about->customAuthorPlainText() + QLatin1Char('\n'));
0935                     }
0936                 }
0937                 exit(0);
0938             } else {
0939                 if (option.startsWith("no")) { // krazy:exclude=strings
0940                     bool noHasParameter = false;
0941                     Q_FOREACH (const QByteArray &name, appOptions->d->options.d->names) {
0942                         if (name.contains(option + QByteArray(" ")) && name.contains('<')) {
0943                             noHasParameter = true;
0944                             break;
0945                         }
0946                     }
0947                     if (!noHasParameter) {
0948                         option = option.mid(2);
0949                         enabled = false;
0950                     }
0951                 }
0952                 staticObj()->findOption(orig, option, i, enabled, inOptions);
0953             }
0954         } else {
0955             // Check whether appOptions allows these arguments
0956             if (!allowArgs) {
0957                 if (staticObj()->ignoreUnknown) {
0958                     continue;
0959                 }
0960                 KCmdLineArgs::enable_i18n();
0961                 KCmdLineArgs::usageError(i18n("Unexpected argument '%1'.", staticObj()->escape(staticObj()->decodeInput(staticObj()->all_argv[i]))));
0962             } else {
0963                 appOptions->d->addArgument(staticObj()->all_argv[i]);
0964                 if (everythingAfterArgIsArgs) {
0965                     inOptions = false;
0966                 }
0967             }
0968         }
0969     }
0970     staticObj()->parsed = true;
0971 }
0972 
0973 int &KCmdLineArgs::qtArgc()
0974 {
0975     if (!staticObj()->argsList) {
0976         addStdCmdLineOptions(CmdLineArgKDE | CmdLineArgQt);    // Lazy bastards!
0977     }
0978 
0979     static int qt_argc = -1;
0980     if (qt_argc != -1) {
0981         return qt_argc;
0982     }
0983 
0984     if (!(staticObj()->mStdargs & KCmdLineArgs::CmdLineArgQt)) {
0985         qt_argc = 2;
0986         return qt_argc;
0987     }
0988 
0989     KCmdLineArgs *args = parsedArgs("qt");
0990     Q_ASSERT(args); // No qt options have been added!
0991     if (!staticObj()->all_argv) {
0992         fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
0993         fprintf(stderr, "Application has not called KCmdLineArgs::init(...).\n\n");
0994 
0995         assert(0);
0996         exit(255);
0997     }
0998 
0999     Q_ASSERT(staticObj()->all_argc >= (args->count() + 1));
1000     qt_argc = args->count() + 1;
1001     return qt_argc;
1002 }
1003 
1004 static char **s_qt_argv;
1005 
1006 char **
1007 KCmdLineArgs::qtArgv()
1008 {
1009     if (!staticObj()->argsList) {
1010         addStdCmdLineOptions(CmdLineArgKDE | CmdLineArgQt);    // Lazy bastards!
1011     }
1012 
1013     if (s_qt_argv != nullptr) {
1014         return s_qt_argv;
1015     }
1016 
1017     if (!(staticObj()->mStdargs & KCmdLineArgs::CmdLineArgQt)) {
1018         s_qt_argv = new char *[2];
1019         s_qt_argv[0] = qstrdup(staticObj()->all_argc ? staticObj()->all_argv[0] : "");
1020         s_qt_argv[1] = nullptr;
1021 
1022         return s_qt_argv;
1023     }
1024 
1025     KCmdLineArgs *args = parsedArgs("qt");
1026     if (!args) {
1027         fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
1028         fprintf(stderr, "The \"qt\" options have not be added to KCmdLineArgs!\n\n");
1029 
1030         assert(0);
1031         exit(255);
1032     }
1033     if (!staticObj()->all_argv) {
1034         fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
1035         fprintf(stderr, "Application has not called KCmdLineArgs::init(...).\n\n");
1036 
1037         assert(0);
1038         exit(255);
1039     }
1040 
1041     int count = args->count();
1042     s_qt_argv = new char *[ count + 2 ];
1043     s_qt_argv[0] = qstrdup(staticObj()->all_argc ? staticObj()->all_argv[0] : "");
1044     int i = 0;
1045     for (; i < count; i++) {
1046         s_qt_argv[i + 1] = qstrdup(args->d->parsedArgList->at(i).data());
1047     }
1048     s_qt_argv[i + 1] = nullptr;
1049 
1050     return s_qt_argv;
1051 }
1052 
1053 const K4AboutData *
1054 KCmdLineArgs::aboutData()
1055 {
1056     return staticObj()->about;
1057 }
1058 
1059 void
1060 KCmdLineArgs::enable_i18n()
1061 {
1062 #pragma message ("KDE5 NOTE: What about this method ?")
1063 }
1064 
1065 void
1066 KCmdLineArgs::usageError(const QString &error)
1067 {
1068     QByteArray localError = staticObj()->encodeOutput(error);
1069     if (localError.endsWith('\n')) {
1070         localError.chop(1);
1071     }
1072     fprintf(stderr, "%s: %s\n", staticObj()->appName, localError.data());
1073 
1074     QString tmp = i18n("Use --help to get a list of available command line options.");
1075     localError = staticObj()->encodeOutput(tmp);
1076     fprintf(stderr, "%s: %s\n", staticObj()->appName, localError.data());
1077     exit(254);
1078 }
1079 
1080 void
1081 KCmdLineArgs::usage(const QByteArray &id)
1082 {
1083     enable_i18n();
1084     Q_ASSERT(staticObj()->argsList != nullptr); // It's an error to call usage(...) without
1085     // having done addCmdLineOptions first!
1086 
1087     QString optionFormatString = QString::fromLatin1("  %1 %2\n");
1088     QString optionFormatStringDef = QString::fromLatin1("  %1 %2 [%3]\n");
1089     QString tmp;
1090     QString usage;
1091 
1092     KCmdLineArgsList::Iterator args = --(staticObj()->argsList->end());
1093 
1094     if ((*args)->d->id.isEmpty() && ((*args)->d->options.d->names.size() > 0) &&
1095             !(*args)->d->options.d->names[0].startsWith('+')) {
1096         usage = i18n("[options] ") + usage;
1097     }
1098 
1099     while (true) {
1100         if (!(*args)->d->name.isEmpty()) {
1101             usage = i18n("[%1-options]", (*args)->d->name.toString()) + QLatin1Char(' ') + usage;
1102         }
1103         if (args == staticObj()->argsList->begin()) {
1104             break;
1105         }
1106         --args;
1107     }
1108 
1109     KCmdLineArgs *appOptions = staticObj()->argsList->last();
1110     if (appOptions->d->id.isEmpty()) {
1111         const KCmdLineOptions &option = appOptions->d->options;
1112         for (int i = 0; i < option.d->names.size(); i++) {
1113             QByteArray opt_name = option.d->names[i];
1114             if (opt_name.startsWith('+')) {
1115                 usage += QString::fromLatin1(opt_name.mid(1).data()) + QLatin1Char(' ');
1116             } else if (opt_name.startsWith("!+")) {
1117                 usage += QString::fromLatin1(opt_name.mid(2).data()) + QLatin1Char(' ');
1118             }
1119         }
1120     }
1121 
1122     staticObj()->printQ(i18n("Usage: %1 %2\n", QString::fromLocal8Bit(staticObj()->appName), staticObj()->escape(usage)));
1123     staticObj()->printQ(QLatin1Char('\n') + staticObj()->about->shortDescription() + QLatin1Char('\n'));
1124 
1125     staticObj()->printQ(i18n("\nGeneric options:\n"));
1126     staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--help"), -25)
1127                         .arg(i18n("Show help about options")));
1128 
1129     args = staticObj()->argsList->begin();
1130     while (args != staticObj()->argsList->end()) {
1131         if (!(*args)->d->name.isEmpty() && !(*args)->d->id.isEmpty()) {
1132             QString option = QString::fromLatin1("--help-%1").arg(QString::fromLatin1((*args)->d->id.data()));
1133             QString desc = i18n("Show %1 specific options", (*args)->d->name.toString());
1134 
1135             staticObj()->printQ(optionFormatString.arg(option, -25).arg(desc));
1136         }
1137         ++args;
1138     }
1139 
1140     staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--help-all"), -25).arg(i18n("Show all options")));
1141     staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--author"), -25).arg(i18n("Show author information")));
1142     staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("-v, --version"), -25).arg(i18n("Show version information")));
1143     staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--license"), -25).arg(i18n("Show license information")));
1144     staticObj()->printQ(optionFormatString.arg(QString::fromLatin1("--"), -25).arg(i18n("End of options")));
1145 
1146     args = staticObj()->argsList->begin(); // Sets current to 1st.
1147 
1148     bool showAll = (id == "all");
1149 
1150     if (!showAll) {
1151         while (args != staticObj()->argsList->end()) {
1152             if (id == (*args)->d->id) {
1153                 break;
1154             }
1155             ++args;
1156         }
1157     }
1158 
1159     while (args != staticObj()->argsList->end()) {
1160         bool hasArgs = false;
1161         bool hasOptions = false;
1162         QString optionsHeader;
1163         if (!(*args)->d->name.isEmpty()) {
1164             optionsHeader = i18n("\n%1 options:\n", (*args)->d->name.toString());
1165         } else {
1166             optionsHeader = i18n("\nOptions:\n");
1167         }
1168 
1169         while (args != staticObj()->argsList->end()) {
1170             const KCmdLineOptions &option = (*args)->d->options;
1171             QByteArray opt;
1172 
1173             for (int i = 0; i < option.d->names.size(); i++) {
1174                 QString description;
1175                 QStringList dl;
1176 
1177                 QString descriptionFull;
1178                 if (!option.d->descriptions[i].isEmpty()) {
1179                     descriptionFull = option.d->descriptions[i].toString();
1180                 }
1181 
1182                 // Option header
1183                 if (option.d->names[i].startsWith(':')) {
1184                     if (!descriptionFull.isEmpty()) {
1185                         optionsHeader = QLatin1Char('\n') + descriptionFull;
1186                         if (!optionsHeader.endsWith(QLatin1Char('\n'))) {
1187                             optionsHeader.append(QLatin1Char('\n'));
1188                         }
1189                         hasOptions = false;
1190                     }
1191                     continue;
1192                 }
1193 
1194                 // Free-form comment
1195                 if (option.d->names[i].isEmpty()) {
1196                     if (!descriptionFull.isEmpty()) {
1197                         tmp = QLatin1Char('\n') + descriptionFull;
1198                         if (!tmp.endsWith(QLatin1Char('\n'))) {
1199                             tmp.append(QLatin1Char('\n'));
1200                         }
1201                         staticObj()->printQ(tmp);
1202                     }
1203                     continue;
1204                 }
1205 
1206                 // Options
1207                 if (!descriptionFull.isEmpty()) {
1208                     dl = descriptionFull.split(QLatin1Char('\n'), QString::KeepEmptyParts);
1209                     description = dl.first();
1210                     dl.erase(dl.begin());
1211                 }
1212                 QByteArray name = option.d->names[i];
1213                 if (name.startsWith('!')) {
1214                     name = name.mid(1);
1215                 }
1216 
1217                 if (name.startsWith('+')) {
1218                     if (!hasArgs) {
1219                         staticObj()->printQ(i18n("\nArguments:\n"));
1220                         hasArgs = true;
1221                     }
1222 
1223                     name = name.mid(1);
1224                     if (name.startsWith('[') && name.endsWith(']')) {
1225                         name = name.mid(1, name.length() - 2);
1226                     }
1227                     staticObj()->printQ(optionFormatString.arg(QString::fromLocal8Bit(name.data()), -25).arg(description));
1228                 } else {
1229                     if (!hasOptions) {
1230                         staticObj()->printQ(optionsHeader);
1231                         hasOptions = true;
1232                     }
1233 
1234                     if ((name.length() == 1) || (name[1] == ' ')) {
1235                         name = '-' + name;
1236                     } else {
1237                         name = "--" + name;
1238                     }
1239                     if (descriptionFull.isEmpty()) {
1240                         opt = name + ", ";
1241                     } else {
1242                         opt = opt + name;
1243                         if (option.d->defaults[i].isEmpty()) {
1244                             staticObj()->printQ(optionFormatString.arg(QString::fromLatin1(opt.data()), -25).arg(description));
1245                         } else {
1246                             staticObj()->printQ(optionFormatStringDef.arg(QString::fromLatin1(opt.data()), -25)
1247                                                 .arg(description, option.d->defaults[i]));
1248                         }
1249                         opt.clear();
1250                     }
1251                 }
1252                 for (QStringList::Iterator it = dl.begin();
1253                         it != dl.end();
1254                         ++it) {
1255                     staticObj()->printQ(optionFormatString.arg(QString(), -25).arg(*it));
1256                 }
1257             }
1258 
1259             ++args;
1260             if (args == staticObj()->argsList->end() || !(*args)->d->name.isEmpty() || (*args)->d->id.isEmpty()) {
1261                 break;
1262             }
1263         }
1264         if (!showAll) {
1265             break;
1266         }
1267     }
1268 
1269     exit(0);
1270 }
1271 
1272 //
1273 // Member functions
1274 //
1275 
1276 /**
1277  *  Constructor.
1278  *
1279  *  The given arguments are assumed to be constants.
1280  */
1281 KCmdLineArgs::KCmdLineArgs(const KCmdLineOptions &_options,
1282                            const KLocalizedString &_name,
1283                            const QByteArray &_id)
1284     : d(new KCmdLineArgsPrivate(_options, _name, _id))
1285 {
1286 }
1287 
1288 /**
1289  *  Destructor.
1290  */
1291 KCmdLineArgs::~KCmdLineArgs()
1292 {
1293     if (!staticObj.isDestroyed() && staticObj()->argsList) {
1294         staticObj()->argsList->removeAll(this);
1295     }
1296     delete d;
1297 }
1298 
1299 void
1300 KCmdLineArgs::setCwd(const QByteArray &cwd)
1301 {
1302     staticObj()->mCwd = cwd;
1303 }
1304 
1305 void
1306 KCmdLineArgs::clear()
1307 {
1308     delete d->parsedArgList;     d->parsedArgList = nullptr;
1309     delete d->parsedOptionList;  d->parsedOptionList = nullptr;
1310 }
1311 
1312 void
1313 KCmdLineArgs::reset()
1314 {
1315     delete staticObj()->argsList; staticObj()->argsList = nullptr;
1316     staticObj()->parsed = false;
1317 }
1318 
1319 void
1320 KCmdLineArgsPrivate::save(QDataStream &ds) const
1321 {
1322     if (parsedOptionList) {
1323         ds << (*(parsedOptionList));
1324     } else {
1325         ds << quint32(0);
1326     }
1327 
1328     if (parsedArgList) {
1329         ds << (*(parsedArgList));
1330     } else {
1331         ds << quint32(0);
1332     }
1333 }
1334 
1335 void
1336 KCmdLineArgsPrivate::load(QDataStream &ds)
1337 {
1338     if (!parsedOptionList) {
1339         parsedOptionList = new KCmdLineParsedOptions;
1340     }
1341     if (!parsedArgList) {
1342         parsedArgList = new KCmdLineParsedArgs;
1343     }
1344 
1345     ds >> (*(parsedOptionList));
1346     ds >> (*(parsedArgList));
1347 
1348     if (parsedOptionList->count() == 0) {
1349         delete parsedOptionList;  parsedOptionList = nullptr;
1350     }
1351     if (parsedArgList->count() == 0) {
1352         delete parsedArgList;     parsedArgList = nullptr;
1353     }
1354 }
1355 
1356 void
1357 KCmdLineArgsPrivate::setOption(const QByteArray &opt, bool enabled)
1358 {
1359     if (isQt) {
1360         // Qt does it own parsing.
1361         QByteArray argString = "-"; // krazy:exclude=doublequote_chars
1362         if (!enabled) {
1363             argString += "no";
1364         }
1365         argString += opt;
1366         addArgument(argString);
1367     }
1368     if (!parsedOptionList) {
1369         parsedOptionList = new KCmdLineParsedOptions;
1370     }
1371 
1372     if (enabled) {
1373         parsedOptionList->insert(opt, "t");    // krazy:exclude=doublequote_chars
1374     } else {
1375         parsedOptionList->insert(opt, "f");    // krazy:exclude=doublequote_chars
1376     }
1377 }
1378 
1379 void
1380 KCmdLineArgsPrivate::setOption(const QByteArray &opt, const QByteArray &value)
1381 {
1382     if (isQt) {
1383         // Qt does it's own parsing.
1384         QByteArray argString = "-"; // krazy:exclude=doublequote_chars
1385         argString += opt;
1386         if (opt == "qmljsdebugger") {
1387             // hack: Qt expects the value of the "qmljsdebugger" option to be
1388             // passed using a '=' separator rather than a space, so we recreate it
1389             // correctly.
1390             // See code of QCoreApplicationPrivate::processCommandLineArguments()
1391             addArgument(argString + "=" + value);
1392         } else {
1393             addArgument(argString);
1394             addArgument(value);
1395         }
1396 
1397 #if HAVE_X11
1398         // Hack coming up!
1399         if (argString == "-display") {
1400             qputenv(DISPLAY, value);
1401         }
1402 #endif
1403     }
1404     if (!parsedOptionList) {
1405         parsedOptionList = new KCmdLineParsedOptions;
1406     }
1407 
1408     parsedOptionList->insertMulti(opt, value);
1409 }
1410 
1411 QString
1412 KCmdLineArgs::getOption(const QByteArray &_opt) const
1413 {
1414     QByteArray opt = _opt;
1415     QByteArray value;
1416     if (d->parsedOptionList) {
1417         value = d->parsedOptionList->value(opt);
1418     }
1419     if (!value.isEmpty()) {
1420         return QString::fromLocal8Bit(value.data());
1421     }
1422 
1423     // Look up the default.
1424     QByteArray opt_name;
1425     QString def;
1426     bool dummy = true;
1427     int result = staticObj()->findOption(d->options, opt, opt_name, def, dummy) & ~4;
1428 
1429     if (result != 3) {
1430         fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
1431         fprintf(stderr, "Application requests for getOption(\"%s\") but the \"%s\" option\n",
1432                 opt.data(), opt.data());
1433         fprintf(stderr, "has never been specified via addCmdLineOptions( ... )\n\n");
1434 
1435         Q_ASSERT(0);
1436         exit(255);
1437     }
1438     return def;
1439 }
1440 
1441 QStringList
1442 KCmdLineArgs::getOptionList(const QByteArray &opt) const
1443 {
1444     QStringList result;
1445     if (!d->parsedOptionList) {
1446         return result;
1447     }
1448 
1449     while (true) {
1450         QByteArray value = d->parsedOptionList->take(opt);
1451         if (value.isEmpty()) {
1452             break;
1453         }
1454         result.prepend(QString::fromLocal8Bit(value.data()));
1455     }
1456 
1457     // Reinsert items in dictionary
1458     // WABA: This is rather silly, but I don't want to add restrictions
1459     // to the API like "you can only call this function once".
1460     // I can't access all items without taking them out of the list.
1461     // So taking them out and then putting them back is the only way.
1462     Q_FOREACH (const QString &str, result) {
1463         d->parsedOptionList->insertMulti(opt, str.toLocal8Bit());
1464     }
1465     return result;
1466 }
1467 
1468 bool
1469 KCmdLineArgs::isSet(const QByteArray &_opt) const
1470 {
1471     // Look up the default.
1472     QByteArray opt = _opt;
1473     QByteArray opt_name;
1474     QString def;
1475     int result = 0;
1476     KCmdLineArgsList::Iterator args = staticObj()->argsList->begin();
1477     while (args != staticObj()->argsList->end()) {
1478         bool dummy = true;
1479         result = staticObj()->findOption((*args)->d->options, opt, opt_name, def, dummy) & ~4;
1480         if (result) {
1481             break;
1482         }
1483         ++args;
1484     }
1485 
1486     if (result == 0) {
1487         fprintf(stderr, "\n\nFAILURE (KCmdLineArgs):\n");
1488         fprintf(stderr, "Application requests for isSet(\"%s\") but the \"%s\" option\n",
1489                 opt.data(), opt.data());
1490         fprintf(stderr, "has never been specified via addCmdLineOptions( ... )\n\n");
1491 
1492         Q_ASSERT(0);
1493         exit(255);
1494     }
1495 
1496     QByteArray value;
1497     if (d->parsedOptionList) {
1498         value = d->parsedOptionList->value(opt);
1499     }
1500 
1501     if (!value.isEmpty()) {
1502         if (result == 3) {
1503             return true;
1504         } else {
1505             return (value.at(0) == 't');
1506         }
1507     }
1508 
1509     if (result == 3) {
1510         return false;    // String option has 'false' as default.
1511     }
1512 
1513     // We return 'true' as default if the option was listed as '-nofork'
1514     // We return 'false' as default if the option was listed as '-fork'
1515     return (result == 2);
1516 }
1517 
1518 int
1519 KCmdLineArgs::count() const
1520 {
1521     return d->parsedArgList ? d->parsedArgList->count() : 0;
1522 }
1523 
1524 QString
1525 KCmdLineArgs::arg(int n) const
1526 {
1527     if (!d->parsedArgList || (n >= (int) d->parsedArgList->count())) {
1528         fprintf(stderr, "\n\nFAILURE (KCmdLineArgs): Argument out of bounds\n");
1529         fprintf(stderr, "Application requests for arg(%d) without checking count() first.\n",
1530                 n);
1531 
1532         Q_ASSERT(0);
1533         exit(255);
1534     }
1535 
1536     return QString::fromLocal8Bit(d->parsedArgList->at(n).data());
1537 }
1538 
1539 QUrl
1540 KCmdLineArgs::url(int n) const
1541 {
1542     return makeURL(arg(n).toUtf8());
1543 }
1544 
1545 QUrl KCmdLineArgs::makeURL(const QByteArray &_urlArg)
1546 {
1547     const QString urlArg = QString::fromUtf8(_urlArg.data());
1548     QFileInfo fileInfo(urlArg);
1549     if (!fileInfo.isRelative()) { // i.e. starts with '/', on unix
1550         QUrl result = QUrl::fromLocalFile(QDir::fromNativeSeparators(urlArg));
1551         return result; // Absolute path.
1552     }
1553 
1554     QUrl qurl(urlArg);
1555     if (qurl.isRelative() || fileInfo.exists()) {
1556         QUrl result = QUrl::fromLocalFile(cwd() + QLatin1Char('/') + urlArg);
1557 #if 0 //Qt5 TODO: QUrlInfo::cleanPath
1558         result.cleanPath(); //This did use KUrl::cleanPath()
1559 #endif
1560         return result;  // Relative path
1561     }
1562 
1563     return QUrl(urlArg); // Argument is a URL
1564 }
1565 
1566 void
1567 KCmdLineArgsPrivate::addArgument(const QByteArray &argument)
1568 {
1569     if (!parsedArgList) {
1570         parsedArgList = new KCmdLineParsedArgs;
1571     }
1572 
1573     parsedArgList->append(argument);
1574 }
1575 
1576 void
1577 KCmdLineArgs::addTempFileOption()
1578 {
1579     KCmdLineOptions tmpopt;
1580     tmpopt.add("tempfile", ki18n("The files/URLs opened by the application will be deleted after use"));
1581     KCmdLineArgs::addCmdLineOptions(tmpopt, ki18n("KDE-tempfile"), "kde-tempfile");
1582 }
1583 
1584 bool KCmdLineArgs::isTempFileSet()
1585 {
1586     KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde-tempfile");
1587     return args && args->isSet("tempfile");
1588 }
1589 
1590 QStringList KCmdLineArgs::allArguments()
1591 {
1592     QStringList lst;
1593 
1594     for (int i = 0; i < staticObj()->all_argc; i++) {
1595         char *arg = staticObj()->all_argv[i];
1596         if (!arg) {
1597             continue;
1598         }
1599         lst.append(QString::fromLocal8Bit(arg));
1600     }
1601     return lst;
1602 }