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 }