File indexing completed on 2024-04-28 15:27:14

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2015 Christoph Cullmann <cullmann@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <QCommandLineParser>
0009 #include <QCoreApplication>
0010 #include <QDir>
0011 #include <QFile>
0012 #include <QFileInfo>
0013 #include <QJsonDocument>
0014 #include <QRegularExpression>
0015 #include <QVariant>
0016 
0017 #include <KConfig>
0018 #include <KConfigGroup>
0019 
0020 int main(int argc, char *argv[])
0021 {
0022     // construct app and set some version
0023     QCoreApplication app(argc, argv);
0024     app.setApplicationVersion(QStringLiteral("1.0"));
0025 
0026     // generic parser config
0027     QCommandLineParser parser;
0028     parser.addVersionOption();
0029     parser.setApplicationDescription(QStringLiteral("Converts .protocol files to json"));
0030     parser.addHelpOption();
0031 
0032     // -o/--output
0033     const QCommandLineOption outputOption(QStringList{QStringLiteral("o"), QStringLiteral("output")},
0034                                           QStringLiteral("Output file name for JSON data."),
0035                                           QStringLiteral("name"));
0036     parser.addOption(outputOption);
0037 
0038     // files to open
0039     parser.addPositionalArgument(QStringLiteral("files"), QStringLiteral(".protocol files to read."), QStringLiteral("[files...]"));
0040 
0041     // parse
0042     parser.process(app);
0043 
0044     // we need an output file
0045     const QString output = parser.value(QStringLiteral("output"));
0046     if (output.isEmpty()) {
0047         qFatal("No output file given, please add --output <name>.");
0048     }
0049 
0050     // we need input files
0051     if (parser.positionalArguments().isEmpty()) {
0052         qFatal("No input file given, please add <files>.");
0053     }
0054 
0055     // different attributes to copy
0056     const QStringList stringAttributes({QStringLiteral("protocol"),
0057                                         QStringLiteral("exec"),
0058                                         QStringLiteral("fileNameUsedForCopying"),
0059                                         QStringLiteral("defaultMimetype"),
0060                                         QStringLiteral("Icon"),
0061                                         QStringLiteral("config"),
0062                                         QStringLiteral("input"),
0063                                         QStringLiteral("output"),
0064                                         QStringLiteral("X-DocPath"),
0065                                         QStringLiteral("DocPath"),
0066                                         QStringLiteral("Class"),
0067                                         QStringLiteral("ProxiedBy")});
0068 
0069     const QStringList stringListAttributes(
0070         {QStringLiteral("listing"), QStringLiteral("archiveMimetype"), QStringLiteral("ExtraTypes"), QStringLiteral("Capabilities")});
0071 
0072     const QStringList boolAttributes({QStringLiteral("source"),
0073                                       QStringLiteral("helper"),
0074                                       QStringLiteral("reading"),
0075                                       QStringLiteral("writing"),
0076                                       QStringLiteral("makedir"),
0077                                       QStringLiteral("deleting"),
0078                                       QStringLiteral("linking"),
0079                                       QStringLiteral("moving"),
0080                                       QStringLiteral("opening"),
0081                                       QStringLiteral("truncating"),
0082                                       QStringLiteral("copyFromFile"),
0083                                       QStringLiteral("copyToFile"),
0084                                       QStringLiteral("renameFromFile"),
0085                                       QStringLiteral("renameToFile"),
0086                                       QStringLiteral("deleteRecursive"),
0087                                       QStringLiteral("determineMimetypeFromExtension"),
0088                                       QStringLiteral("ShowPreviews")});
0089 
0090     const QStringList intAttributes({QStringLiteral("maxInstances"), QStringLiteral("maxInstancesPerHost")});
0091 
0092     const QStringList translatedStringListAttributes({QStringLiteral("ExtraNames")});
0093 
0094     // constructed the json data by parsing all .protocol files
0095     QVariantMap protocolsData;
0096     const QRegularExpression localeRegex(QStringLiteral("^\\w+\\[(.*)\\]="));
0097     const QStringList args = parser.positionalArguments();
0098     for (const QString &file : args) {
0099         // get full path
0100         const QString fullFilePath(QDir::current().absoluteFilePath(file));
0101 
0102         // construct kconfig for protocol file
0103         KConfig sconfig(fullFilePath);
0104         sconfig.setLocale(QString());
0105         KConfigGroup config(&sconfig, "Protocol");
0106 
0107         // name must be set, sanity check that file got read
0108         const QString name(config.readEntry("protocol"));
0109         if (name.isEmpty()) {
0110             qFatal("Failed to read input file %s.", qPrintable(fullFilePath));
0111         }
0112 
0113         // construct protocol data
0114         QVariantMap protocolData;
0115 
0116         // convert the different types
0117         for (const QString &key : stringAttributes) {
0118             if (config.hasKey(key)) {
0119                 protocolData.insert(key, config.readEntry(key, QString()));
0120             }
0121         }
0122         for (const QString &key : stringListAttributes) {
0123             if (config.hasKey(key)) {
0124                 protocolData.insert(key, config.readEntry(key, QStringList()));
0125             }
0126         }
0127         for (const QString &key : boolAttributes) {
0128             if (config.hasKey(key)) {
0129                 protocolData.insert(key, config.readEntry(key, bool(false)));
0130             }
0131         }
0132         for (const QString &key : intAttributes) {
0133             if (config.hasKey(key)) {
0134                 protocolData.insert(key, config.readEntry(key, int(0)));
0135             }
0136         }
0137 
0138         // handle translated keys
0139         for (const QString &key : translatedStringListAttributes) {
0140             // read untranslated entry first in any case!
0141             sconfig.setLocale(QString());
0142             const QStringList untranslatedValues = config.readEntry(key, QStringList());
0143             if (!untranslatedValues.isEmpty()) {
0144                 protocolData.insert(key, untranslatedValues);
0145             }
0146 
0147             // collect all locales for this key
0148             QFile f(fullFilePath);
0149             QStringList foundLocalesForKey;
0150             if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
0151                 qFatal("Failed to open file %s.", qPrintable(fullFilePath));
0152             }
0153             while (!f.atEnd()) {
0154                 const QString line = QString::fromUtf8(f.readLine());
0155                 const QRegularExpressionMatch match = localeRegex.match(line);
0156                 if (match.hasMatch()) {
0157                     foundLocalesForKey.append(match.captured(1));
0158                 }
0159             }
0160 
0161             // insert all entries for all found locales, reparse config for this
0162             for (const QString &locale : std::as_const(foundLocalesForKey)) {
0163                 sconfig.setLocale(locale);
0164                 protocolData.insert(QStringLiteral("%1[%2]").arg(key, locale), config.readEntry(key, QStringList()));
0165             }
0166         }
0167 
0168         // use basename of protocol for toplevel map in json, like it is done for .protocol files
0169         const QString baseName(QFileInfo(fullFilePath).baseName());
0170         protocolsData.insert(baseName, protocolData);
0171     }
0172 
0173     // pack in our namespace
0174     QVariantMap jsonData;
0175     jsonData.insert(QStringLiteral("KDE-KIO-Protocols"), protocolsData);
0176 
0177     // create outfile, after all has worked!
0178     QFile outFile(output);
0179     if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0180         qFatal("Failed to open output file %s.", qPrintable(output));
0181     }
0182 
0183     // write out json
0184     outFile.write(QJsonDocument::fromVariant(QVariant(jsonData)).toJson());
0185 
0186     // be done
0187     return 0;
0188 }