main.cpp 12.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* This file is part of the KDE project
   Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "kwrite.h"
22
#include "kwriteapplication.h"
23 24 25 26 27 28

#include <ktexteditor/document.h>
#include <ktexteditor/view.h>
#include <ktexteditor/editor.h>

#include <KAboutData>
29
#include <kcoreaddons_version.h> // for KAboutData::setDesktopFileName()
30 31 32
#include <KLocalizedString>
#include <KMessageBox>
#include <KDBusService>
33 34 35 36
#include <kcrash_version.h>
#if KCrash_VERSION >= QT_VERSION_CHECK(5, 15, 0)
#include <KCrash>
#endif // KCrash >= 5.15
37 38 39 40 41 42

#include <QCommandLineParser>
#include <QApplication>
#include <QTextCodec>
#include <QFileInfo>
#include <QDir>
43
#include <QUrlQuery>
44

45 46
#include "../urlinfo.h"

47 48 49 50 51
#ifndef Q_OS_WIN
#include <unistd.h>
#endif
#include <iostream>

Christoph Cullmann's avatar
Christoph Cullmann committed
52
extern "C" Q_DECL_EXPORT int main(int argc, char **argv)
53
{
54
#ifndef Q_OS_WIN
55
    // Prohibit using sudo or kdesu (but allow using the root user directly)
56
    if (getuid() == 0) {
57
        if (!qEnvironmentVariableIsEmpty("SUDO_USER")) {
58
            std::cout << "Executing KWrite with sudo is not possible due to unfixable security vulnerabilities." << std::endl;
59 60
            return EXIT_FAILURE;
        } else if (!qEnvironmentVariableIsEmpty("KDESU_USER")) {
61
            std::cout << "Executing KWrite with kdesu is not possible due to unfixable security vulnerabilities." << std::endl;
62 63
            return EXIT_FAILURE;
        }
64 65
    }
#endif
66
    /**
67 68
     * Create application first
     * Enforce application name even if the executable is renamed
69 70
     */
    QApplication app(argc, argv);
71
    app.setApplicationName(QStringLiteral("kwrite"));
72

73 74 75 76 77
    /**
     * enable high dpi support
     */
    app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);

78 79 80 81 82 83 84
    /**
     * Enable crash handling through KCrash.
     */
#if KCrash_VERSION >= QT_VERSION_CHECK(5, 15, 0)
    KCrash::initialize();
#endif

85 86 87 88 89
    /**
     * Connect application with translation catalogs
     */
    KLocalizedString::setApplicationDomain("kwrite");

90 91 92
    /**
     * then use i18n and co
     */
93
    KAboutData aboutData(QStringLiteral("kwrite"),
94
                         i18n("KWrite"),
95
                         QStringLiteral(KATE_VERSION),
96
                         i18n("KWrite - Text Editor"), KAboutLicense::LGPL_V2,
97
                         i18n("(c) 2000-2019 The Kate Authors"), QString(), QStringLiteral("https://kate-editor.org"));
98 99 100 101 102 103

    /**
     * right dbus prefix == org.kde.
     */
    aboutData.setOrganizationDomain(QByteArray("kde.org"));

104 105 106 107 108 109 110
    /**
     * desktop file association to make application icon work (e.g. in Wayland window decoration)
     */
#if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 16, 0)
    aboutData.setDesktopFileName(QStringLiteral("org.kde.kwrite"));
#endif

111
    aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("https://cullmann.io"));
112
    aboutData.addAuthor(i18n("Dominik Haumann"), i18n("Core Developer"), QStringLiteral("dhaumann@kde.org"));
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    aboutData.addAuthor(i18n("Anders Lund"), i18n("Core Developer"), QStringLiteral("anders@alweb.dk"), QStringLiteral("http://www.alweb.dk"));
    aboutData.addAuthor(i18n("Joseph Wenninger"), i18n("Core Developer"), QStringLiteral("jowenn@kde.org"), QStringLiteral("http://stud3.tuwien.ac.at/~e9925371"));
    aboutData.addAuthor(i18n("Hamish Rodda"), i18n("Core Developer"), QStringLiteral("rodda@kde.org"));
    aboutData.addAuthor(i18n("Waldo Bastian"), i18n("The cool buffersystem"), QStringLiteral("bastian@kde.org"));
    aboutData.addAuthor(i18n("Charles Samuels"), i18n("The Editing Commands"), QStringLiteral("charles@kde.org"));
    aboutData.addAuthor(i18n("Matt Newell"), i18nc("Credit text for someone that did testing and some other similar things", "Testing, ..."), QStringLiteral("newellm@proaxis.com"));
    aboutData.addAuthor(i18n("Michael Bartl"), i18n("Former Core Developer"), QStringLiteral("michael.bartl1@chello.at"));
    aboutData.addAuthor(i18n("Michael McCallum"), i18n("Core Developer"), QStringLiteral("gholam@xtra.co.nz"));
    aboutData.addAuthor(i18n("Jochen Wilhemly"), i18n("KWrite Author"), QStringLiteral("digisnap@cs.tu-berlin.de"));
    aboutData.addAuthor(i18n("Michael Koch"), i18n("KWrite port to KParts"), QStringLiteral("koch@kde.org"));
    aboutData.addAuthor(i18n("Christian Gebauer"), QString(), QStringLiteral("gebauer@kde.org"));
    aboutData.addAuthor(i18n("Simon Hausmann"), QString(), QStringLiteral("hausmann@kde.org"));
    aboutData.addAuthor(i18n("Glen Parker"), i18n("KWrite Undo History, Kspell integration"), QStringLiteral("glenebob@nwlink.com"));
    aboutData.addAuthor(i18n("Scott Manson"), i18n("KWrite XML Syntax highlighting support"), QStringLiteral("sdmanson@alltel.net"));
    aboutData.addAuthor(i18n("John Firebaugh"), i18n("Patches and more"), QStringLiteral("jfirebaugh@kde.org"));
    aboutData.addAuthor(i18n("Gerald Senarclens de Grancy"), i18n("QA and Scripting"), QStringLiteral("oss@senarclens.eu"), QStringLiteral("http://find-santa.eu/"));

    aboutData.addCredit(i18n("Matteo Merli"), i18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), QStringLiteral("merlim@libero.it"));
    aboutData.addCredit(i18n("Rocky Scaletta"), i18n("Highlighting for VHDL"), QStringLiteral("rocky@purdue.edu"));
    aboutData.addCredit(i18n("Yury Lebedev"), i18n("Highlighting for SQL"));
    aboutData.addCredit(i18n("Chris Ross"), i18n("Highlighting for Ferite"));
    aboutData.addCredit(i18n("Nick Roux"), i18n("Highlighting for ILERPG"));
    aboutData.addCredit(i18n("Carsten Niehaus"), i18n("Highlighting for LaTeX"));
    aboutData.addCredit(i18n("Per Wigren"), i18n("Highlighting for Makefiles, Python"));
    aboutData.addCredit(i18n("Jan Fritz"), i18n("Highlighting for Python"));
    aboutData.addCredit(i18n("Daniel Naber"));
    aboutData.addCredit(i18n("Roland Pabel"), i18n("Highlighting for Scheme"));
    aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("PHP Keyword/Datatype list"));
    aboutData.addCredit(i18n("Carsten Pfeiffer"), i18nc("Credit text for someone that helped a lot", "Very nice help"));
    aboutData.addCredit(i18n("All people who have contributed and I have forgotten to mention"));

Christoph Cullmann's avatar
Christoph Cullmann committed
144 145 146
    /**
     * bugzilla
     */
147 148 149
    aboutData.setProductName(QByteArray("kate/kwrite"));

    /**
150
     * set and register app about data
151 152 153
     */
    KAboutData::setApplicationData(aboutData);

Christoph Cullmann's avatar
Christoph Cullmann committed
154 155 156
    /**
     * set the program icon
     */
Laurent Montel's avatar
Laurent Montel committed
157
    QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("accessories-text-editor"), app.windowIcon()));
158

159 160 161 162 163 164 165
    /**
     * Create command line parser and feed it with known options
     */
    QCommandLineParser parser;
    aboutData.setupCommandLine(&parser);

    // -e/--encoding option
166
    const QCommandLineOption useEncoding(QStringList() << QStringLiteral("e") << QStringLiteral("encoding"), i18n("Set encoding for the file to open."), i18n("encoding"));
167 168 169
    parser.addOption(useEncoding);

    // -l/--line option
170
    const QCommandLineOption gotoLine(QStringList() << QStringLiteral("l") << QStringLiteral("line"), i18n("Navigate to this line."), i18n("line"));
171 172 173
    parser.addOption(gotoLine);

    // -c/--column option
174
    const QCommandLineOption gotoColumn(QStringList() << QStringLiteral("c") << QStringLiteral("column"), i18n("Navigate to this column."), i18n("column"));
175 176 177 178 179 180 181 182 183 184 185
    parser.addOption(gotoColumn);

    // -i/--stdin option
    const QCommandLineOption readStdIn(QStringList() << QStringLiteral("i") << QStringLiteral("stdin"), i18n("Read the contents of stdin."));
    parser.addOption(readStdIn);

    // --tempfile option
    const QCommandLineOption tempfile(QStringList() << QStringLiteral("tempfile"), i18n("The files/URLs opened by the application will be deleted after use"));
    parser.addOption(tempfile);

    // urls to open
186
    parser.addPositionalArgument(QStringLiteral("urls"), i18n("Documents to open."), i18n("[urls...]"));
187 188 189 190 191 192 193 194 195 196 197

    /**
     * do the command line parsing
     */
    parser.process(app);

    /**
     * handle standard options
     */
    aboutData.processCommandLine(&parser);

198 199
    KWriteApplication kapp;

200
    if (app.isSessionRestored()) {
201
        kapp.restore();
202 203 204 205
    } else {
        bool nav = false;
        int line = 0, column = 0;

206
        QTextCodec *codec = parser.isSet(QStringLiteral("encoding")) ? QTextCodec::codecForName(parser.value(QStringLiteral("encoding")).toLocal8Bit()) : nullptr;
207 208 209 210 211

        if (parser.isSet(QStringLiteral("line"))) {
            line = parser.value(QStringLiteral("line")).toInt() - 1;
            nav = true;
        }
212

213 214 215 216
        if (parser.isSet(QStringLiteral("column"))) {
            column = parser.value(QStringLiteral("column")).toInt() - 1;
            nav = true;
        }
217

218
        if (parser.positionalArguments().count() == 0) {
219
            KWrite *t = kapp.newWindow();
220

221 222
            if (parser.isSet(QStringLiteral("stdin"))) {
                QTextStream input(stdin, QIODevice::ReadOnly);
223

224 225 226 227
                // set chosen codec
                if (codec) {
                    input.setCodec(codec);
                }
228

229 230
                QString line;
                QString text;
231

232 233 234 235
                do {
                    line = input.readLine();
                    text.append(line + QLatin1Char('\n'));
                } while (!line.isNull());
236

237
                KTextEditor::Document *doc = t->activeView()->document();
238
                if (doc) {
239 240 241 242
                    // remember codec in document, e.g. to show the right one
                    if (codec) {
                        doc->setEncoding(QString::fromLatin1(codec->name()));
                    }
243 244 245
                    doc->setText(text);
                }
            }
246

247 248
            if (nav && t->activeView()) {
                t->activeView()->setCursorPosition(KTextEditor::Cursor(line, column));
249
            }
250
        } else {
251 252
            int docs_opened = 0;
            Q_FOREACH(const QString positionalArgument, parser.positionalArguments()) {
253 254 255
                UrlInfo info(positionalArgument);
                if (nav) {
                    info.cursor = KTextEditor::Cursor(line, column);
256 257 258
                }

                // this file is no local dir, open it, else warn
259
                bool noDir = !info.url.isLocalFile() || !QFileInfo(info.url.toLocalFile()).isDir();
260 261 262

                if (noDir) {
                    ++docs_opened;
263
                    KWrite *t = kapp.newWindow();
264 265

                    if (codec) {
266
                        t->activeView()->document()->setEncoding(QString::fromLatin1(codec->name()));
267 268
                    }

269
                    t->loadURL(info.url);
270

271
                    if (info.cursor.isValid()) {
272
                        t->activeView()->setCursorPosition(info.cursor);
273
                    }
274 275 276 277 278 279 280 281 282 283 284 285 286
                    else if (info.url.hasQuery()) {
                        QUrlQuery q(info.url);
                        QString lineStr = q.queryItemValue(QStringLiteral("line"));
                        QString columnStr = q.queryItemValue(QStringLiteral("column"));

                        line = lineStr.toInt();
                        if (line > 0)
                            line--;

                        column = columnStr.toInt();
                        if (column > 0)
                            column--;

287
                        t->activeView()->setCursorPosition(KTextEditor::Cursor(line, column));
288
                    }
289
                } else {
290
                    KMessageBox::sorry(nullptr, i18n("The file '%1' could not be opened: it is not a normal file, it is a folder.", info.url.toString()));
291 292 293 294 295
                }
            }
            if (!docs_opened) {
                ::exit(1);    // see http://bugs.kde.org/show_bug.cgi?id=124708
            }
296
        }
297
    }
298

299 300
    // no window there, uh, ohh, for example borked session config !!!
    // create at least one !!
301 302
    if (kapp.noWindows()) {
        kapp.newWindow();
303
    }
304

305
    /**
306
     * finally register this kwrite instance for dbus, don't die if no dbus is around!
307
     */
308
    const KDBusService dbusService(KDBusService::Multiple | KDBusService::NoExitOnFailure);
309

310 311 312 313
    /**
     * Run the event loop
     */
    return app.exec();
314
}