Warning, file /utilities/kdebugsettings/src/kdebugsettingsutil.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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