File indexing completed on 2023-09-24 04:04:46
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 * &, <, >, ', ", 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("&"); 0667 } else if (c == QLatin1Char('<')) { 0668 ntext += QLatin1String("<"); 0669 } else if (c == QLatin1Char('>')) { 0670 ntext += QLatin1String(">"); 0671 } else if (c == QLatin1Char('\'')) { 0672 ntext += QLatin1String("'"); // not handled by Qt::escape 0673 } else if (c == QLatin1Char('"')) { 0674 ntext += QLatin1String("""); 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(" <") + (*it).emailAddress() + QLatin1String(">"); 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 }