File indexing completed on 2024-05-12 05:52:34

0001 /*
0002     SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 
0006 */
0007 
0008 #include "kdebugsettingsutil.h"
0009 #include "kdebugsettingscore_debug.h"
0010 #include <KLocalizedString>
0011 #include <QDir>
0012 #include <QFile>
0013 #include <QRegularExpression>
0014 #include <QStandardPaths>
0015 
0016 RenameCategory KDebugSettingsUtil::parseRenameCategory(QString line, const QString &filename)
0017 {
0018     RenameCategory category;
0019     int pos = line.indexOf(QLatin1Char('#'));
0020     if (pos != -1) {
0021         line.truncate(pos);
0022         line = line.simplified();
0023     }
0024 
0025     if (line.isEmpty()) {
0026         return category;
0027     }
0028     line = line.simplified();
0029     const int space = line.indexOf(QLatin1Char(' '));
0030     if (space == -1) {
0031         qCWarning(KDEBUGSETTINGSCORE_LOG) << "Invalid categories file. Missing space. Syntax is logname<space>description + optional element. Line: " << line
0032                                           << " from file:" << filename << Qt::endl;
0033         return category;
0034     }
0035 
0036     const QString originalName = line.left(space);
0037 
0038     const QString newName = line.mid(space).simplified();
0039     category.originalName = originalName;
0040     category.newName = newName;
0041     return category;
0042 }
0043 
0044 RenameCategory::List KDebugSettingsUtil::readRenameCategories(const QString &filename)
0045 {
0046     RenameCategory::List insertCategories;
0047 
0048     QFile file(filename);
0049     if (!file.open(QIODevice::ReadOnly)) {
0050         qCWarning(KDEBUGSETTINGSCORE_LOG) << "Couldn't open" << filename;
0051     } else {
0052         QString data;
0053         QTextStream ts(&file);
0054         ts.setEncoding(QStringConverter::Encoding::Latin1);
0055         while (!ts.atEnd()) {
0056             data = ts.readLine().simplified();
0057             const RenameCategory category = parseRenameCategory(data, filename);
0058             if (category.isValid()) {
0059                 insertCategories.append(category);
0060             }
0061         }
0062     }
0063     return insertCategories;
0064 }
0065 
0066 KdeLoggingCategory KDebugSettingsUtil::parseLineKdeLoggingCategory(QString line, const QString &filename)
0067 {
0068     KdeLoggingCategory category;
0069     const int pos = line.indexOf(QLatin1Char('#'));
0070     if (pos != -1) {
0071         line.truncate(pos);
0072         line = line.simplified();
0073     }
0074 
0075     if (line.isEmpty()) {
0076         return category;
0077     }
0078     line = line.simplified();
0079     const int space = line.indexOf(QLatin1Char(' '));
0080     if (space == -1) {
0081         qCWarning(KDEBUGSETTINGSCORE_LOG) << "Invalid categories file. Missing space. Syntax is logname<space>description + optional element. Line: " << line
0082                                           << " from file:" << filename << Qt::endl;
0083         return category;
0084     }
0085     QString logName;
0086     QString description;
0087     QString defaultSeverity;
0088     QString identifier;
0089 
0090     // TODO create an unique regularexpression
0091 
0092     static const QRegularExpression regularExpressionUser(QStringLiteral("^([\\w._-]+)\\s*(.*)$"));
0093     QRegularExpressionMatch match = regularExpressionUser.match(line);
0094     if (match.hasMatch()) {
0095         logName = match.captured(1);
0096         description = match.captured(2);
0097     }
0098 
0099     bool newFormatFound = false;
0100     static const QRegularExpression regularExpressionDefaultSeverityNewFormat(
0101         QStringLiteral("^(.*)\\s+DEFAULT_SEVERITY\\s+\\[(DEBUG|INFO|WARNING|CRITICAL)\\](?:\\s+(.*))?"));
0102     QRegularExpressionMatch match3 = regularExpressionDefaultSeverityNewFormat.match(description);
0103     QString defaultSeverityCaptured;
0104     QString potentialIdentifier;
0105     if (match3.hasMatch()) {
0106         newFormatFound = true;
0107         const QString descriptionCaptured = match3.captured(1);
0108         defaultSeverityCaptured = match3.captured(2);
0109         potentialIdentifier = match3.captured(3);
0110         if (!descriptionCaptured.isEmpty() && !defaultSeverityCaptured.isEmpty()) {
0111             description = descriptionCaptured;
0112             defaultSeverity = defaultSeverityCaptured;
0113             // qDebug() << " match.captured(1);" << descriptionCaptured;
0114             // qDebug() << " match.captured(2);" << defaultCategoryCaptured;
0115         }
0116     }
0117 
0118     if (potentialIdentifier.isEmpty()) {
0119         static const QRegularExpression regularExpressionDefaultIdentifierNewFormat(QStringLiteral("^(.*)\\s+IDENTIFIER\\s+\\[(.*)\\]"));
0120         QRegularExpressionMatch match4 = regularExpressionDefaultIdentifierNewFormat.match(description);
0121         if (match4.hasMatch()) {
0122             newFormatFound = true;
0123             const QString descriptionCaptured = match4.captured(1);
0124             const QString identifierCaptured = match4.captured(2);
0125             if (!descriptionCaptured.isEmpty() && !identifierCaptured.isEmpty()) {
0126                 description = descriptionCaptured;
0127                 identifier = identifierCaptured;
0128                 // qDebug() << " match.captured(1);" << descriptionCaptured;
0129                 // qDebug() << " match.captured(2);" << identifierCaptured;
0130             }
0131         }
0132     } else {
0133         static const QRegularExpression regularExpressionDefaultIdentifierNewFormat2(QStringLiteral("IDENTIFIER\\s+\\[(.*)\\]"));
0134         QRegularExpressionMatch match4 = regularExpressionDefaultIdentifierNewFormat2.match(potentialIdentifier);
0135         if (match4.hasMatch()) {
0136             newFormatFound = true;
0137             const QString identifierCaptured = match4.captured(1);
0138             if (!identifierCaptured.isEmpty()) {
0139                 identifier = identifierCaptured;
0140             }
0141         }
0142     }
0143 
0144     if (!newFormatFound) {
0145         // Old format.
0146         static const QRegularExpression regularExpressionDefaultSeverityOldFormat(QStringLiteral("^(.*)\\s+\\[(DEBUG|INFO|WARNING|CRITICAL)\\]"));
0147         QRegularExpressionMatch match2 = regularExpressionDefaultSeverityOldFormat.match(description);
0148         if (match2.hasMatch()) {
0149             const QString descriptionCaptured = match2.captured(1);
0150             defaultSeverityCaptured = match2.captured(2);
0151             if (!descriptionCaptured.isEmpty() && !defaultSeverityCaptured.isEmpty()) {
0152                 description = descriptionCaptured;
0153                 defaultSeverity = defaultSeverityCaptured;
0154                 // qDebug() << " match.captured(1);" << descriptionCaptured;
0155                 // qDebug() << " match.captured(2);" << defaultCategoryCaptured;
0156                 qCWarning(KDEBUGSETTINGSCORE_LOG) << "In this file: " << filename << " this line " << line << " still uses the old format. We need to port it.";
0157             }
0158         } else {
0159             qCWarning(KDEBUGSETTINGSCORE_LOG) << "In this file: " << filename << " this line " << line << " still uses the old format. We need to port it.";
0160         }
0161     }
0162     category.categoryName = logName;
0163     category.description = description;
0164     category.defaultSeverity = defaultSeverity;
0165     category.identifierName = identifier;
0166     return category;
0167 }
0168 
0169 KdeLoggingCategory::List KDebugSettingsUtil::readLoggingCategoriesForInserting(const QString &filename, KdeLoggingCategory::List &categoriesList)
0170 {
0171     KdeLoggingCategory::List insertCategories;
0172 
0173     QFile file(filename);
0174     if (!file.open(QIODevice::ReadOnly)) {
0175         qCWarning(KDEBUGSETTINGSCORE_LOG) << "Couldn't open" << filename;
0176     } else {
0177         QString data;
0178         QTextStream ts(&file);
0179         ts.setEncoding(QStringConverter::Encoding::Latin1);
0180         while (!ts.atEnd()) {
0181             data = ts.readLine().simplified();
0182             const KdeLoggingCategory category = parseLineKdeLoggingCategory(data, filename);
0183             if (category.isValid()) {
0184                 bool needToAppend = true;
0185                 for (const KdeLoggingCategory &cat : std::as_const(categoriesList)) {
0186                     if (cat == category) {
0187                         needToAppend = false;
0188                         break;
0189                     } else if (cat.categoryName == category.categoryName) {
0190                         qCWarning(KDEBUGSETTINGSCORE_LOG)
0191                             << "Duplicate categories, it is a bug. Please verify: category:" << cat.categoryName << " filename : " << filename;
0192                         needToAppend = false;
0193                     }
0194                 }
0195                 if (needToAppend) {
0196                     categoriesList.append(category);
0197                     insertCategories.append(category);
0198                 }
0199             }
0200         }
0201     }
0202     return insertCategories;
0203 }
0204 
0205 void KDebugSettingsUtil::readLoggingCategories(const QString &filename, KdeLoggingCategory::List &categoriesList, bool checkCategoryList)
0206 {
0207     QFile file(filename);
0208     if (!file.open(QIODevice::ReadOnly)) {
0209         qCWarning(KDEBUGSETTINGSCORE_LOG) << "Couldn't open" << filename;
0210     } else {
0211         QString data;
0212         QTextStream ts(&file);
0213         ts.setEncoding(QStringConverter::Encoding::Latin1);
0214         while (!ts.atEnd()) {
0215             data = ts.readLine().simplified();
0216             const KdeLoggingCategory category = parseLineKdeLoggingCategory(data, filename);
0217             if (category.isValid()) {
0218                 if (checkCategoryList) {
0219                     bool needToAppend = true;
0220                     for (const KdeLoggingCategory &cat : std::as_const(categoriesList)) {
0221                         if (cat == category) {
0222                             needToAppend = false;
0223                             break;
0224                         } else if (cat.categoryName == category.categoryName) {
0225                             qCWarning(KDEBUGSETTINGSCORE_LOG)
0226                                 << "Duplicate categories, it is a bug. Please verify: category:" << cat.categoryName << " filename : " << filename;
0227                             needToAppend = false;
0228                         }
0229                     }
0230                     if (needToAppend) {
0231                         categoriesList.append(category);
0232                     }
0233                 } else {
0234                     categoriesList.append(category);
0235                 }
0236             }
0237         }
0238     }
0239 }
0240 
0241 KDebugSettingsUtil::LineLoggingQtCategory KDebugSettingsUtil::parseLineLoggingQtCategory(const QString &line)
0242 {
0243     LineLoggingQtCategory lineCategory;
0244     int equalPos = line.indexOf(QLatin1Char('='));
0245     if ((equalPos != -1) && (line.lastIndexOf(QLatin1Char('=')) == equalPos)) {
0246         const QString pattern = line.left(equalPos);
0247         const QString valueStr = line.mid(equalPos + 1);
0248         if (valueStr == QLatin1StringView("true")) {
0249             lineCategory.enabled = true;
0250         } else {
0251             lineCategory.enabled = false;
0252         }
0253         QString p;
0254         if (pattern.endsWith(QLatin1StringView(".debug"))) {
0255             p = pattern.left(pattern.length() - 6); // strlen(".debug")
0256             lineCategory.logName = p;
0257             lineCategory.type = LineLoggingQtCategory::Debug;
0258         } else if (pattern.endsWith(QLatin1StringView(".warning"))) {
0259             p = pattern.left(pattern.length() - 8); // strlen(".warning")
0260             lineCategory.logName = p;
0261             lineCategory.type = LineLoggingQtCategory::Warning;
0262         } else if (pattern.endsWith(QLatin1StringView(".critical"))) {
0263             p = pattern.left(pattern.length() - 9); // strlen(".critical")
0264             lineCategory.logName = p;
0265             lineCategory.type = LineLoggingQtCategory::Critical;
0266         } else if (pattern.endsWith(QLatin1StringView(".info"))) {
0267             p = pattern.left(pattern.length() - 5); // strlen(".info")
0268             lineCategory.logName = p;
0269             lineCategory.type = LineLoggingQtCategory::Info;
0270         } else {
0271             p = pattern;
0272             lineCategory.logName = p;
0273             lineCategory.type = LineLoggingQtCategory::All;
0274         }
0275     } else {
0276         lineCategory.enabled = false;
0277     }
0278     return lineCategory;
0279 }
0280 
0281 QList<KDebugSettingsUtil::LoadLoggingCategory> KDebugSettingsUtil::readLoggingQtCategories(const QString &filename)
0282 {
0283     if (filename.isEmpty()) {
0284         qCWarning(KDEBUGSETTINGSCORE_LOG) << "Empty file name";
0285         return {};
0286     }
0287     // Code based on src/corelib/io/qloggingregistry.cpp
0288     QFile file(filename);
0289     QMap<QString, KDebugSettingsUtil::LoadLoggingCategory> hashLoadLoggingCategories;
0290     if (file.open(QIODevice::ReadOnly)) {
0291         QTextStream ts(&file);
0292         QString _section;
0293         bool rulesSections = false;
0294         while (!ts.atEnd()) {
0295             QString line = ts.readLine();
0296 
0297             // Remove all whitespace from line
0298             line = line.simplified();
0299             line.remove(QLatin1Char(' '));
0300 
0301             // comment
0302             if (line.startsWith(QLatin1Char(';'))) {
0303                 continue;
0304             }
0305 
0306             if (line.startsWith(QLatin1Char('[')) && line.endsWith(QLatin1Char(']'))) {
0307                 // new section
0308                 _section = line.mid(1, line.size() - 2);
0309                 rulesSections = (_section == QLatin1StringView("Rules"));
0310                 continue;
0311             }
0312             if (rulesSections) {
0313                 KDebugSettingsUtil::LineLoggingQtCategory cat = parseLineLoggingQtCategory(line);
0314                 // qDebug() << " line " << line;
0315                 // qDebug() << "enable " << cat.enabled << " valid : " << cat.isValid();
0316                 if (cat.isValid()) {
0317                     KDebugSettingsUtil::LoadLoggingCategory nextCat = hashLoadLoggingCategories.value(cat.logName);
0318                     if (nextCat.isValid()) {
0319                         LoadLoggingCategory::LogType type = LoadLoggingCategory::Unknown;
0320                         switch (cat.type) {
0321                         case LineLoggingQtCategory::Unknown:
0322                             type = LoadLoggingCategory::Unknown;
0323                             break;
0324                         case LineLoggingQtCategory::Info:
0325                             type = LoadLoggingCategory::Info;
0326                             break;
0327                         case LineLoggingQtCategory::Warning:
0328                             type = LoadLoggingCategory::Warning;
0329                             break;
0330                         case LineLoggingQtCategory::Debug:
0331                             type = LoadLoggingCategory::Debug;
0332                             break;
0333                         case LineLoggingQtCategory::Critical:
0334                             type = LoadLoggingCategory::Critical;
0335                             break;
0336                         case LineLoggingQtCategory::All:
0337                             type = LoadLoggingCategory::All;
0338                             break;
0339                         }
0340 
0341                         if (nextCat.loggingTypes.contains(type)) {
0342                             nextCat.loggingTypes[type] = cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled;
0343                         } else {
0344                             nextCat.loggingTypes.insert(type, cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled);
0345                         }
0346                         hashLoadLoggingCategories[cat.logName] = nextCat;
0347                     } else {
0348                         nextCat.logName = cat.logName;
0349                         switch (cat.type) {
0350                         case LineLoggingQtCategory::Unknown:
0351                             nextCat.loggingTypes.insert(LoadLoggingCategory::Unknown,
0352                                                         cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled);
0353                             break;
0354                         case LineLoggingQtCategory::Info:
0355                             nextCat.loggingTypes.insert(LoadLoggingCategory::Info, cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled);
0356                             break;
0357                         case LineLoggingQtCategory::Warning:
0358                             nextCat.loggingTypes.insert(LoadLoggingCategory::Warning,
0359                                                         cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled);
0360                             break;
0361                         case LineLoggingQtCategory::Debug:
0362                             nextCat.loggingTypes.insert(LoadLoggingCategory::Debug, cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled);
0363                             break;
0364                         case LineLoggingQtCategory::Critical:
0365                             nextCat.loggingTypes.insert(LoadLoggingCategory::Critical,
0366                                                         cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled);
0367                             break;
0368                         case LineLoggingQtCategory::All:
0369                             nextCat.loggingTypes.insert(LoadLoggingCategory::All, cat.enabled ? LoadLoggingCategory::Enabled : LoadLoggingCategory::Disabled);
0370                             break;
0371                         }
0372                         hashLoadLoggingCategories.insert(cat.logName, nextCat);
0373                     }
0374                 }
0375             }
0376         }
0377     } else {
0378         qCWarning(KDEBUGSETTINGSCORE_LOG) << "Impossible to open file: " << filename;
0379     }
0380 
0381     return hashLoadLoggingCategories.values();
0382 }
0383 
0384 LoggingCategory::LoggingType KDebugSettingsUtil::convertCategoryTypeFromString(const QString &str)
0385 {
0386     if (str.isEmpty()) {
0387         return LoggingCategory::Info; // Default
0388     } else if (str == QLatin1StringView("DEBUG")) {
0389         return LoggingCategory::Debug;
0390     } else if (str == QLatin1StringView("INFO")) {
0391         return LoggingCategory::Info;
0392     } else if (str == QLatin1StringView("WARNING")) {
0393         return LoggingCategory::Warning;
0394     } else if (str == QLatin1StringView("CRITICAL")) {
0395         return LoggingCategory::Critical;
0396     }
0397     qCWarning(KDEBUGSETTINGSCORE_LOG) << "Default category is unknown: " << str;
0398     return LoggingCategory::Info;
0399 }
0400 
0401 QString KDebugSettingsUtil::convertCategoryTypeToString(LoggingCategory::LoggingType type)
0402 {
0403     QString str;
0404     switch (type) {
0405     case LoggingCategory::Debug:
0406     case LoggingCategory::All:
0407         str = i18n("Full Debug");
0408         break;
0409     case LoggingCategory::Info:
0410         str = i18n("Info");
0411         break;
0412     case LoggingCategory::Warning:
0413         str = i18n("Warning");
0414         break;
0415     case LoggingCategory::Critical:
0416         str = i18n("Critical");
0417         break;
0418     case LoggingCategory::Off:
0419         str = i18n("Off");
0420         break;
0421     case LoggingCategory::Undefined:
0422         str = i18n("Undefined");
0423         break;
0424     }
0425     return str;
0426 }
0427 
0428 QString KDebugSettingsUtil::qtFileName()
0429 {
0430     const QString envPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1StringView("/QtProject");
0431     QDir().mkpath(envPath);
0432     const QString qtloggingFileName = envPath + QStringLiteral("/qtlogging.ini");
0433     return qtloggingFileName;
0434 }
0435 
0436 QString KDebugSettingsUtil::defaultWritableGroupPath()
0437 {
0438     return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1StringView("/groups");
0439 }
0440 
0441 QStringList KDebugSettingsUtil::groupFileList()
0442 {
0443     const QString groupPath = KDebugSettingsUtil::defaultWritableGroupPath();
0444     if (groupPath.isEmpty()) {
0445         return {};
0446     }
0447     QDir dir(groupPath);
0448     const QStringList groups = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
0449     return groups;
0450 }
0451 
0452 bool KDebugSettingsUtil::hasWritableGroups()
0453 {
0454     return !groupFileList().isEmpty();
0455 }
0456 
0457 LoggingCategory KDebugSettingsUtil::convertRuleStrToLoggingCategory(const QString &ruleStr)
0458 {
0459     LoggingCategory tmp;
0460     const KDebugSettingsUtil::LineLoggingQtCategory cat = KDebugSettingsUtil::parseLineLoggingQtCategory(ruleStr);
0461     if (cat.isValid()) {
0462         tmp.categoryName = cat.logName;
0463         tmp.enabled = cat.enabled;
0464         switch (cat.type) {
0465         case KDebugSettingsUtil::LineLoggingQtCategory::Unknown:
0466             break;
0467         case KDebugSettingsUtil::LineLoggingQtCategory::Info:
0468             tmp.loggingType = LoggingCategory::Info;
0469             break;
0470         case KDebugSettingsUtil::LineLoggingQtCategory::Warning:
0471             tmp.loggingType = LoggingCategory::Warning;
0472             break;
0473         case KDebugSettingsUtil::LineLoggingQtCategory::Debug:
0474             tmp.loggingType = LoggingCategory::Debug;
0475             break;
0476         case KDebugSettingsUtil::LineLoggingQtCategory::Critical:
0477             tmp.loggingType = LoggingCategory::Critical;
0478             break;
0479         case KDebugSettingsUtil::LineLoggingQtCategory::All:
0480             tmp.loggingType = LoggingCategory::All;
0481             break;
0482         }
0483     }
0484     return tmp;
0485 }
0486 
0487 QString KDebugSettingsUtil::generateDisplayRule(const QString &categoryName, bool state, LoggingCategory::LoggingType type)
0488 {
0489     QString ruleStr = categoryName;
0490     if (!categoryName.isEmpty()) {
0491         switch (type) {
0492         case LoggingCategory::Undefined:
0493         case LoggingCategory::All:
0494             break;
0495         case LoggingCategory::Info:
0496             ruleStr += QStringLiteral(".info");
0497             break;
0498         case LoggingCategory::Warning:
0499             ruleStr += QStringLiteral(".warning");
0500             break;
0501         case LoggingCategory::Debug:
0502             ruleStr += QStringLiteral(".debug");
0503             break;
0504         case LoggingCategory::Critical:
0505             ruleStr += QStringLiteral(".critical");
0506             break;
0507         case LoggingCategory::Off:
0508             break;
0509         }
0510         if (state) {
0511             ruleStr += QStringLiteral("=true");
0512         } else {
0513             ruleStr += QStringLiteral("=false");
0514         }
0515     }
0516     return ruleStr;
0517 }