File indexing completed on 2024-04-28 17:01:01

0001 // clang-format off
0002 /*
0003  *  This file is part of KDiff3.
0004  *
0005  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
0006  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 // clang-format on
0010 
0011 #include "kdiff3_shell.h"
0012 #include "TypeUtils.h"
0013 #include "version.h"
0014 
0015 #include <stdio.h>  // for fileno, stderr
0016 #include <stdlib.h> // for exit
0017 
0018 #ifndef Q_OS_WIN
0019 #include <unistd.h>
0020 #endif
0021 
0022 #include <KAboutData>
0023 #include <KCrash>
0024 #include <KLocalizedString>
0025 #include <KMessageBox>
0026 
0027 #include <QApplication>
0028 #include <QCommandLineOption>
0029 #include <QCommandLineParser>
0030 #include <QFile>
0031 #include <QScopedPointer>
0032 #include <QStandardPaths>
0033 #include <QStringList>
0034 #include <QTextStream>
0035 
0036 void initialiseCmdLineArgs(QCommandLineParser* cmdLineParser)
0037 {
0038     const QString configFileName = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, "kdiff3rc");
0039     QFile configFile(configFileName);
0040     QString ignorableOptionsLine = "-u;-query;-html;-abort";
0041     if(configFile.open(QIODevice::ReadOnly))
0042     {
0043         QTextStream ts(&configFile);
0044         while(!ts.atEnd())
0045         {
0046             const QString line = ts.readLine();
0047             if(line.startsWith(u8"IgnorableCmdLineOptions="))
0048             {
0049                 const QtSizeType pos = line.indexOf('=');
0050                 if(pos >= 0)
0051                 {
0052                     ignorableOptionsLine = line.mid(pos + 1);
0053                 }
0054                 break;
0055             }
0056         }
0057     }
0058 
0059     const QStringList ignorableOptions = ignorableOptionsLine.split(';');
0060 
0061     for(QString ignorableOption: ignorableOptions)
0062     {
0063         ignorableOption.remove('-');
0064         if(!ignorableOption.isEmpty())
0065         {
0066             if(ignorableOption.length() == 1)
0067             {
0068                 cmdLineParser->addOption(QCommandLineOption({ignorableOption, u8"ignore"}, i18n("Ignored. (User defined.)")));
0069             }
0070             else
0071             {
0072                 cmdLineParser->addOption(QCommandLineOption(ignorableOption, i18n("Ignored. (User defined.)")));
0073             }
0074         }
0075     }
0076 }
0077 
0078 qint32 main(qint32 argc, char* argv[])
0079 {
0080     constexpr QLatin1String appName("kdiff3", sizeof("kdiff3") - 1);
0081     //Syncronize qt HDPI behavoir on all versions/platforms
0082     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0083     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0084     QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
0085 
0086     QApplication app(argc, argv); // KAboutData and QCommandLineParser depend on this being setup.
0087     KLocalizedString::setApplicationDomain(appName.data());
0088 
0089     KCrash::initialize();
0090 
0091     const QString i18nName = i18n("KDiff3");
0092     QString appVersion(KDIFF3_VERSION_STRING);
0093 
0094     appVersion += i18nc("Program version info.", " (64 bit)");
0095 
0096     const QString description = i18n("Tool for Comparison and Merge of Files and Folders");
0097     const QString copyright = i18n("(c) 2002-2014 Joachim Eibl, (c) 2017 Michael Reeves KF5/Qt5 port");
0098     const QString homePage = QStringLiteral("https://kde.org/applications/development/kdiff3");
0099 
0100     KAboutData aboutData(appName, i18nName,
0101                          appVersion, description, KAboutLicense::GPL_V2, copyright, QString(),
0102                          homePage);
0103 
0104     KAboutData::setApplicationData(aboutData);
0105 
0106     /*
0107         The QCommandLineParser is a static scoped unique ptr. This is safe given that.
0108         As the distuctor will not be fired until main exits.
0109     */
0110     QCommandLineParser* cmdLineParser = KDiff3Shell::getParser().get();
0111     cmdLineParser->setApplicationDescription(aboutData.shortDescription());
0112 
0113     aboutData.setupCommandLine(cmdLineParser);
0114 
0115     initialiseCmdLineArgs(cmdLineParser);
0116     // ignorable command options
0117     cmdLineParser->addOption(QCommandLineOption({u8"m", u8"merge"}, i18n("Merge the input.")));
0118     cmdLineParser->addOption(QCommandLineOption({u8"b", u8"base"}, i18n("Explicit base file. For compatibility with certain tools."), u8"file"));
0119     cmdLineParser->addOption(QCommandLineOption({u8"o", u8"output"}, i18n("Output file. Implies -m. E.g.: -o newfile.txt"), u8"file"));
0120     cmdLineParser->addOption(QCommandLineOption(u8"out", i18n("Output file, again. (For compatibility with certain tools.)"), u8"file"));
0121 #ifdef ENABLE_AUTO
0122     cmdLineParser->addOption(QCommandLineOption(u8"auto", i18n("No GUI if all conflicts are auto-solvable. (Needs -o file)")));
0123     cmdLineParser->addOption(QCommandLineOption(u8"noauto", i18n("Ignore --auto and always show GUI.")));
0124 #else
0125     cmdLineParser->addOption(QCommandLineOption(u8"noauto", i18n("Ignored.")));
0126     cmdLineParser->addOption(QCommandLineOption(u8"auto", i18n("Ignored.")));
0127 #endif
0128     cmdLineParser->addOption(QCommandLineOption(u8"L1", i18n("Visible name replacement for input file 1 (base)."), u8"alias1"));
0129     cmdLineParser->addOption(QCommandLineOption(u8"L2", i18n("Visible name replacement for input file 2."), u8"alias2"));
0130     cmdLineParser->addOption(QCommandLineOption(u8"L3", i18n("Visible name replacement for input file 3."), u8"alias3"));
0131     cmdLineParser->addOption(QCommandLineOption({u8"L", u8"fname"}, i18n("Alternative visible name replacement. Supply this once for every input."), u8"alias"));
0132     cmdLineParser->addOption(QCommandLineOption(u8"cs", i18n("Override a config setting. Use once for every setting. E.g.: --cs \"AutoAdvance=1\""), u8"string"));
0133     cmdLineParser->addOption(QCommandLineOption(u8"confighelp", i18n("Show list of config settings and current values.")));
0134     cmdLineParser->addOption(QCommandLineOption(u8"config", i18n("Use a different config file."), u8"file"));
0135 
0136     // other command options
0137     cmdLineParser->addPositionalArgument(u8"[File1]", i18n("file1 to open (base, if not specified via --base)"));
0138     cmdLineParser->addPositionalArgument(u8"[File2]", i18n("file2 to open"));
0139     cmdLineParser->addPositionalArgument(u8"[File3]", i18n("file3 to open"));
0140 
0141     bool isAtty = true;
0142 
0143 #ifndef Q_OS_WIN
0144     isAtty = isatty(fileno(stderr)) == 1;//will be true for redirected output as well
0145 #endif
0146     /*
0147         QCommandLineParser::process does what is expected on windows or when running from a commandline.
0148         However, it only accounts for a lack of terminal output on windows.
0149     */
0150     if(isAtty)
0151     {
0152         cmdLineParser->process(QCoreApplication::arguments());
0153     }
0154     else
0155     {
0156         /*
0157             There is no terminal connected so don't just exit mysteriously on error.
0158         */
0159         if(!cmdLineParser->parse(QCoreApplication::arguments()))
0160         {
0161             const QString errorMessage = cmdLineParser->errorText();
0162 
0163             KMessageBox::error(nullptr, "<html><head/><body><h2>" + errorMessage + "</h2><pre>" + i18n("See kdiff3 --help for supported options.") + "</pre></body></html>", aboutData.displayName());
0164             exit(1);
0165         }
0166 
0167         if(cmdLineParser->isSet(QStringLiteral("version")))
0168         {
0169             KMessageBox::information(nullptr,
0170                                     aboutData.displayName() + ' ' + aboutData.version(), aboutData.displayName());
0171             exit(0);
0172         }
0173         if(cmdLineParser->isSet(QStringLiteral("help")))
0174         {
0175             KMessageBox::information(nullptr, "<html><head/><body><pre>" + cmdLineParser->helpText() + "</pre></body></html>", aboutData.displayName());
0176 
0177             exit(0);
0178         }
0179     }
0180 
0181     aboutData.processCommandLine(cmdLineParser);
0182 
0183     /*
0184         This short segment is wrapped in a lambda to delay KDiff3Shell construction until
0185         after the main event loop starts. Thus allowing us to avoid std::exit as much as
0186         possiable. Makes for a cleaner exit.
0187     */
0188     QPointer<KDiff3Shell> p;
0189     QMetaObject::invokeMethod(
0190         qApp, [&p] {
0191             /*
0192               Do not attempt to call show here that will be done later.
0193               This variable exists solely to insure the KDiff3Shell is deleted on exit.
0194             */
0195             p = new KDiff3Shell();
0196         },
0197         Qt::QueuedConnection);
0198     qint32 retVal = QApplication::exec();
0199     delete p;
0200     return retVal;
0201 }