File indexing completed on 2022-09-27 16:31:46

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