refact: improve desktop parsing
This commit is contained in:
parent
a16754647a
commit
3988e0c526
@ -123,7 +123,7 @@ IncludeCategories:
|
|||||||
- Regex: '.*'
|
- Regex: '.*'
|
||||||
Priority: 1
|
Priority: 1
|
||||||
# 缩进case标签
|
# 缩进case标签
|
||||||
IndentCaseLabels: true
|
IndentCaseLabels: false
|
||||||
|
|
||||||
#IndentPPDirectives: AfterHash
|
#IndentPPDirectives: AfterHash
|
||||||
# 缩进宽度
|
# 缩进宽度
|
||||||
|
@ -395,92 +395,92 @@ LaunchTask ApplicationService::unescapeExec(const QString &str, const QStringLis
|
|||||||
auto location = execList.indexOf(codeStr);
|
auto location = execList.indexOf(codeStr);
|
||||||
|
|
||||||
switch (filesCode) {
|
switch (filesCode) {
|
||||||
case 'f': { // Defer to async job
|
case 'f': { // Defer to async job
|
||||||
task.command.append(std::move(execList));
|
task.command.append(std::move(execList));
|
||||||
for (auto &field : fields) {
|
for (auto &field : fields) {
|
||||||
task.Resources.emplace_back(std::move(field));
|
task.Resources.emplace_back(std::move(field));
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'u': {
|
break;
|
||||||
execList.removeAt(location);
|
}
|
||||||
if (fields.empty()) {
|
case 'u': {
|
||||||
task.command.append(execList);
|
execList.removeAt(location);
|
||||||
break;
|
if (fields.empty()) {
|
||||||
}
|
|
||||||
if (fields.count() > 1) {
|
|
||||||
qDebug() << R"(fields count is greater than one, %u will only take first element.)";
|
|
||||||
}
|
|
||||||
execList.insert(location, fields.first());
|
|
||||||
task.command.append(execList);
|
task.command.append(execList);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'F':
|
if (fields.count() > 1) {
|
||||||
[[fallthrough]];
|
qDebug() << R"(fields count is greater than one, %u will only take first element.)";
|
||||||
case 'U': {
|
|
||||||
execList.removeAt(location);
|
|
||||||
auto it = execList.begin() + location;
|
|
||||||
for (const auto &field : fields) {
|
|
||||||
it = execList.insert(it, field);
|
|
||||||
}
|
|
||||||
task.command.append(std::move(execList));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'i': {
|
execList.insert(location, fields.first());
|
||||||
execList.removeAt(location);
|
task.command.append(execList);
|
||||||
auto val = m_entry->value(DesktopFileEntryKey, "Icon");
|
break;
|
||||||
if (!val) {
|
}
|
||||||
qDebug() << R"(Application Icons can't be found. %i will be ignored.)";
|
case 'F':
|
||||||
task.command.append(std::move(execList));
|
[[fallthrough]];
|
||||||
return task;
|
case 'U': {
|
||||||
}
|
execList.removeAt(location);
|
||||||
bool ok;
|
auto it = execList.begin() + location;
|
||||||
auto iconStr = val->toIconString(ok);
|
for (const auto &field : fields) {
|
||||||
if (!ok) {
|
it = execList.insert(it, field);
|
||||||
qDebug() << R"(Icons Convert to string failed. %i will be ignored.)";
|
|
||||||
task.command.append(std::move(execList));
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
auto it = execList.insert(location, iconStr);
|
|
||||||
execList.insert(it, "--icon");
|
|
||||||
task.command.append(std::move(execList));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'c': {
|
task.command.append(std::move(execList));
|
||||||
execList.removeAt(location);
|
break;
|
||||||
auto val = m_entry->value(DesktopFileEntryKey, "Name");
|
}
|
||||||
if (!val) {
|
case 'i': {
|
||||||
qDebug() << R"(Application Name can't be found. %c will be ignored.)";
|
execList.removeAt(location);
|
||||||
task.command.append(std::move(execList));
|
auto val = m_entry->value(DesktopFileEntryKey, "Icon");
|
||||||
return task;
|
if (!val) {
|
||||||
}
|
qDebug() << R"(Application Icons can't be found. %i will be ignored.)";
|
||||||
bool ok;
|
|
||||||
auto NameStr = val->toLocaleString(getUserLocale(), ok);
|
|
||||||
if (!ok) {
|
|
||||||
qDebug() << R"(Name Convert to locale string failed. %c will be ignored.)";
|
|
||||||
task.command.append(std::move(execList));
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
execList.insert(location, NameStr);
|
|
||||||
task.command.append(std::move(execList));
|
task.command.append(std::move(execList));
|
||||||
break;
|
return task;
|
||||||
}
|
}
|
||||||
case 'k': { // ignore all desktop file location for now.
|
bool ok;
|
||||||
execList.removeAt(location);
|
auto iconStr = val->toIconString(ok);
|
||||||
|
if (!ok) {
|
||||||
|
qDebug() << R"(Icons Convert to string failed. %i will be ignored.)";
|
||||||
task.command.append(std::move(execList));
|
task.command.append(std::move(execList));
|
||||||
break;
|
return task;
|
||||||
}
|
}
|
||||||
case 'd':
|
auto it = execList.insert(location, iconStr);
|
||||||
case 'D':
|
execList.insert(it, "--icon");
|
||||||
case 'n':
|
task.command.append(std::move(execList));
|
||||||
case 'N':
|
break;
|
||||||
case 'v':
|
}
|
||||||
[[fallthrough]]; // Deprecated field codes should be removed from the command line and ignored.
|
case 'c': {
|
||||||
case 'm': {
|
execList.removeAt(location);
|
||||||
execList.removeAt(location);
|
auto val = m_entry->value(DesktopFileEntryKey, "Name");
|
||||||
|
if (!val) {
|
||||||
|
qDebug() << R"(Application Name can't be found. %c will be ignored.)";
|
||||||
task.command.append(std::move(execList));
|
task.command.append(std::move(execList));
|
||||||
break;
|
return task;
|
||||||
}
|
}
|
||||||
|
bool ok;
|
||||||
|
auto NameStr = val->toLocaleString(getUserLocale(), ok);
|
||||||
|
if (!ok) {
|
||||||
|
qDebug() << R"(Name Convert to locale string failed. %c will be ignored.)";
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
execList.insert(location, NameStr);
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'k': { // ignore all desktop file location for now.
|
||||||
|
execList.removeAt(location);
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
case 'n':
|
||||||
|
case 'N':
|
||||||
|
case 'v':
|
||||||
|
[[fallthrough]]; // Deprecated field codes should be removed from the command line and ignored.
|
||||||
|
case 'm': {
|
||||||
|
execList.removeAt(location);
|
||||||
|
task.command.append(std::move(execList));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.Resources.isEmpty()) {
|
if (task.Resources.isEmpty()) {
|
||||||
|
@ -16,6 +16,24 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
bool isInvalidLocaleString(const QString &str) noexcept
|
||||||
|
{
|
||||||
|
constexpr auto Language = R"((?:[a-z]+))"; // language of locale postfix. eg.(en, zh)
|
||||||
|
constexpr auto Country = R"((?:_[A-Z]+))"; // country of locale postfix. eg.(US, CN)
|
||||||
|
constexpr auto Encoding = R"((?:\.[0-9A-Z-]+))"; // encoding of locale postfix. eg.(UFT-8)
|
||||||
|
constexpr auto Modifier = R"((?:@[a-z=;]+))"; // modifier of locale postfix. eg.(euro;collation=traditional)
|
||||||
|
const static auto validKey = QString(R"(^%1%2?%3?%4?$)").arg(Language, Country, Encoding, Modifier);
|
||||||
|
// example: https://regex101.com/r/hylOay/2
|
||||||
|
static const QRegularExpression _re = []() -> QRegularExpression {
|
||||||
|
QRegularExpression tmp{validKey};
|
||||||
|
tmp.optimize();
|
||||||
|
return tmp;
|
||||||
|
}();
|
||||||
|
thread_local const auto re = _re;
|
||||||
|
|
||||||
|
return re.match(str).hasMatch();
|
||||||
|
}
|
||||||
|
|
||||||
bool hasNonAsciiAndControlCharacters(const QString &str) noexcept
|
bool hasNonAsciiAndControlCharacters(const QString &str) noexcept
|
||||||
{
|
{
|
||||||
static const QRegularExpression _matchControlChars = []() {
|
static const QRegularExpression _matchControlChars = []() {
|
||||||
@ -36,89 +54,126 @@ bool hasNonAsciiAndControlCharacters(const QString &str) noexcept
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
auto DesktopEntry::parseGroupHeader(const QString &str) noexcept
|
struct Parser
|
||||||
{
|
{
|
||||||
|
Parser(QTextStream &stream)
|
||||||
|
: m_stream(stream){};
|
||||||
|
QTextStream &m_stream;
|
||||||
|
QString m_line;
|
||||||
|
|
||||||
|
using Groups = QMap<QString, QMap<QString, DesktopEntry::Value>>;
|
||||||
|
|
||||||
|
DesktopErrorCode parse(Groups &groups) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void skip() noexcept;
|
||||||
|
DesktopErrorCode addGroup(Groups &groups) noexcept;
|
||||||
|
DesktopErrorCode addEntry(Groups::iterator &group) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Parser::skip() noexcept
|
||||||
|
{
|
||||||
|
while (!m_stream.atEnd() and (m_line.startsWith('#') or m_line.isEmpty())) {
|
||||||
|
m_line = m_stream.readLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopErrorCode Parser::parse(Groups &ret) noexcept
|
||||||
|
{
|
||||||
|
std::remove_reference_t<decltype(ret)> groups;
|
||||||
|
while (!m_stream.atEnd()) {
|
||||||
|
auto err = addGroup(groups);
|
||||||
|
if (err != DesktopErrorCode::NoError) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groups.size() != 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groups.keys().first() != DesktopFileEntryKey) {
|
||||||
|
qWarning() << "There should be nothing preceding "
|
||||||
|
"'Desktop Entry' group in the desktop entry file "
|
||||||
|
"but possibly one or more comments.";
|
||||||
|
return DesktopErrorCode::InvalidFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_line.isEmpty()) {
|
||||||
|
qCritical() << "Something is wrong in Desktop file parser, check logic.";
|
||||||
|
return DesktopErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = std::move(groups);
|
||||||
|
return DesktopErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopErrorCode Parser::addGroup(Groups &ret) noexcept
|
||||||
|
{
|
||||||
|
skip();
|
||||||
|
if (!m_line.startsWith('[')) {
|
||||||
|
qWarning() << "Invalid desktop file format: unexpected line:" << m_line;
|
||||||
|
return DesktopErrorCode::InvalidFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsing group header.
|
||||||
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#group-header
|
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#group-header
|
||||||
|
|
||||||
auto groupHeader = str.sliced(1, str.size() - 2).trimmed();
|
auto groupHeader = m_line.sliced(1, m_line.size() - 2).trimmed();
|
||||||
decltype(m_entryMap)::iterator it{m_entryMap.end()};
|
|
||||||
|
|
||||||
if (groupHeader.contains('[') || groupHeader.contains(']') || hasNonAsciiAndControlCharacters(groupHeader)) {
|
if (groupHeader.contains('[') || groupHeader.contains(']') || hasNonAsciiAndControlCharacters(groupHeader)) {
|
||||||
qWarning() << "group header invalid:" << str;
|
qWarning() << "group header invalid:" << m_line;
|
||||||
return it;
|
return DesktopErrorCode::InvalidFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto tmp = m_entryMap.find(groupHeader);
|
if (ret.find(groupHeader) != ret.end()) {
|
||||||
if (tmp == m_entryMap.end()) {
|
qWarning() << "duplicated group header detected:" << groupHeader;
|
||||||
it = m_entryMap.insert(groupHeader, {});
|
return DesktopErrorCode::InvalidFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
qWarning() << "group header already exists:" << str;
|
auto group = ret.insert(groupHeader, {});
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString DesktopFile::sourcePath() const noexcept
|
m_line.clear();
|
||||||
{
|
while (!m_stream.atEnd() && !m_line.startsWith('[')) {
|
||||||
if (!m_fileSource) {
|
skip();
|
||||||
return "";
|
auto err = addEntry(group);
|
||||||
}
|
if (err != DesktopErrorCode::NoError) {
|
||||||
|
return err;
|
||||||
QFileInfo info(*m_fileSource);
|
|
||||||
return info.absoluteFilePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DesktopEntry::isInvalidLocaleString(const QString &str) noexcept
|
|
||||||
{
|
|
||||||
constexpr auto Language = R"((?:[a-z]+))"; // language of locale postfix. eg.(en, zh)
|
|
||||||
constexpr auto Country = R"((?:_[A-Z]+))"; // country of locale postfix. eg.(US, CN)
|
|
||||||
constexpr auto Encoding = R"((?:\.[0-9A-Z-]+))"; // encoding of locale postfix. eg.(UFT-8)
|
|
||||||
constexpr auto Modifier = R"((?:@[a-z=;]+))"; // modifier of locale postfix. eg.(euro;collation=traditional)
|
|
||||||
const static auto validKey = QString(R"(^%1%2?%3?%4?$)").arg(Language, Country, Encoding, Modifier);
|
|
||||||
// example: https://regex101.com/r/hylOay/2
|
|
||||||
static const QRegularExpression _re = []() -> QRegularExpression {
|
|
||||||
QRegularExpression tmp{validKey};
|
|
||||||
tmp.optimize();
|
|
||||||
return tmp;
|
|
||||||
}();
|
|
||||||
thread_local const auto re = _re;
|
|
||||||
|
|
||||||
return re.match(str).hasMatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
QPair<QString, QString> DesktopEntry::processEntry(const QString &str) noexcept
|
|
||||||
{
|
|
||||||
auto splitCharIndex = str.indexOf(']');
|
|
||||||
if (splitCharIndex != -1) {
|
|
||||||
for (; splitCharIndex < str.size(); ++splitCharIndex) {
|
|
||||||
if (str.at(splitCharIndex) == '=') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
splitCharIndex = str.indexOf('=');
|
|
||||||
}
|
}
|
||||||
auto keyStr = str.first(splitCharIndex).trimmed();
|
return DesktopErrorCode::NoError;
|
||||||
auto valueStr = str.sliced(splitCharIndex + 1).trimmed();
|
|
||||||
return qMakePair(std::move(keyStr), std::move(valueStr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QPair<QString, QString>> DesktopEntry::processEntryKey(const QString &keyStr) noexcept
|
DesktopErrorCode Parser::addEntry(Groups::iterator &group) noexcept
|
||||||
{
|
{
|
||||||
QString key;
|
auto line = m_line;
|
||||||
QString localeStr;
|
m_line.clear();
|
||||||
|
auto splitCharIndex = line.indexOf('=');
|
||||||
|
if (splitCharIndex == -1) {
|
||||||
|
qWarning() << "invalid line in desktop file, skip it:" << line;
|
||||||
|
return DesktopErrorCode::NoError;
|
||||||
|
}
|
||||||
|
auto keyStr = line.first(splitCharIndex).trimmed();
|
||||||
|
auto valueStr = line.sliced(splitCharIndex + 1).trimmed();
|
||||||
|
|
||||||
|
QString key{""};
|
||||||
|
QString localeStr{defaultKeyStr};
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// We are process "localized keys" here, for usage check:
|
// We are process "localized keys" here, for usage check:
|
||||||
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#localized-keys
|
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#localized-keys
|
||||||
if (auto index = keyStr.indexOf('['); index != -1) {
|
qsizetype localeBegin = keyStr.indexOf('[');
|
||||||
key = keyStr.sliced(0, index);
|
qsizetype localeEnd = keyStr.lastIndexOf(']');
|
||||||
localeStr = keyStr.sliced(index + 1, keyStr.length() - 1 - index - 1); // strip '[' and ']'
|
if ((localeBegin == -1 && localeEnd != -1) || (localeBegin != -1 && localeEnd == -1)) {
|
||||||
if (!isInvalidLocaleString(localeStr)) {
|
qWarning() << "unmatched [] detected in desktop file, skip this line: " << line;
|
||||||
qWarning().noquote() << QString("invalid LOCALE (%2) for key \"%1\"").arg(key, localeStr);
|
return DesktopErrorCode::NoError;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
if (localeBegin == -1 && localeEnd == -1) {
|
||||||
key = keyStr;
|
key = keyStr;
|
||||||
|
} else {
|
||||||
|
key = keyStr.sliced(0, localeBegin);
|
||||||
|
localeStr = keyStr.sliced(localeBegin + 1, localeEnd - localeBegin - 1); // strip '[' and ']'
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QRegularExpression _re = []() {
|
static const QRegularExpression _re = []() {
|
||||||
@ -129,44 +184,34 @@ std::optional<QPair<QString, QString>> DesktopEntry::processEntryKey(const QStri
|
|||||||
// NOTE: https://stackoverflow.com/a/25583104
|
// NOTE: https://stackoverflow.com/a/25583104
|
||||||
thread_local const QRegularExpression re = _re;
|
thread_local const QRegularExpression re = _re;
|
||||||
if (re.match(key).hasMatch()) {
|
if (re.match(key).hasMatch()) {
|
||||||
qWarning() << "keyName's format is invalid.";
|
qWarning() << "invalid key name, skip this line:" << line;
|
||||||
return std::nullopt;
|
return DesktopErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
return qMakePair(std::move(key), std::move(localeStr));
|
if (localeStr != defaultKeyStr && !isInvalidLocaleString(localeStr)) {
|
||||||
|
qWarning().noquote() << QString("invalid LOCALE (%2) for key \"%1\"").arg(key, localeStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto keyIt = group->find(key);
|
||||||
|
if (keyIt != group->end() && keyIt->find(localeStr) != keyIt->end()) {
|
||||||
|
qWarning() << "duplicated localestring, skip this line:" << line;
|
||||||
|
return DesktopErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
group->insert(key, {{localeStr, valueStr}});
|
||||||
|
return DesktopErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
DesktopErrorCode DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept
|
} // namespace
|
||||||
|
|
||||||
|
QString DesktopFile::sourcePath() const noexcept
|
||||||
{
|
{
|
||||||
auto [key, value] = processEntry(str);
|
if (!m_fileSource) {
|
||||||
auto keyPair = processEntryKey(key);
|
return "";
|
||||||
|
|
||||||
if (!keyPair.has_value()) {
|
|
||||||
return DesktopErrorCode::InvalidFormat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [keyName, localeStr] = std::move(keyPair).value();
|
QFileInfo info(*m_fileSource);
|
||||||
if (localeStr.isEmpty()) {
|
return info.absoluteFilePath();
|
||||||
localeStr = defaultKeyStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto valueIt = currentGroup->find(keyName);
|
|
||||||
if (valueIt == currentGroup->end()) {
|
|
||||||
currentGroup->insert(keyName, {{localeStr, value}});
|
|
||||||
return DesktopErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto innerIt = valueIt->find(localeStr);
|
|
||||||
if (innerIt == valueIt->end()) {
|
|
||||||
valueIt->insert(localeStr, value);
|
|
||||||
return DesktopErrorCode::NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
qWarning() << "duplicated postfix and this line will be aborted, maybe format is invalid.\n"
|
|
||||||
<< "exist: " << innerIt.key() << "[" << innerIt.value() << "]"
|
|
||||||
<< "current: " << keyName << "[" << localeStr << "]";
|
|
||||||
|
|
||||||
return DesktopErrorCode::NoError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DesktopEntry::checkMainEntryValidation() const noexcept
|
bool DesktopEntry::checkMainEntryValidation() const noexcept
|
||||||
@ -333,86 +378,26 @@ DesktopErrorCode DesktopEntry::parse(DesktopFile &file) noexcept
|
|||||||
return parse(stream);
|
return parse(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DesktopEntry::skipCheck(const QString &line) noexcept
|
|
||||||
{
|
|
||||||
return line.startsWith('#') or line.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
DesktopErrorCode DesktopEntry::parse(QTextStream &stream) noexcept
|
DesktopErrorCode DesktopEntry::parse(QTextStream &stream) noexcept
|
||||||
{
|
{
|
||||||
|
if (m_parsed == true) {
|
||||||
|
return DesktopErrorCode::Parsed;
|
||||||
|
}
|
||||||
|
|
||||||
if (stream.atEnd()) {
|
if (stream.atEnd()) {
|
||||||
if (m_context == EntryContext::Done) {
|
|
||||||
return DesktopErrorCode::NoError;
|
|
||||||
}
|
|
||||||
return DesktopErrorCode::OpenFailed;
|
return DesktopErrorCode::OpenFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.setEncoding(QStringConverter::Utf8);
|
stream.setEncoding(QStringConverter::Utf8);
|
||||||
decltype(m_entryMap)::iterator currentGroup;
|
|
||||||
|
|
||||||
DesktopErrorCode err{DesktopErrorCode::NoError};
|
DesktopErrorCode err{DesktopErrorCode::NoError};
|
||||||
bool mainEntryParsed{false};
|
Parser p(stream);
|
||||||
QString line;
|
err = p.parse(m_entryMap);
|
||||||
|
m_parsed = true;
|
||||||
while (!stream.atEnd()) {
|
if (err != DesktopErrorCode::NoError) {
|
||||||
switch (m_context) {
|
return err;
|
||||||
case EntryContext::Unknown: {
|
|
||||||
qWarning() << "entry context is unknown,abort.";
|
|
||||||
err = DesktopErrorCode::InvalidFormat;
|
|
||||||
return err;
|
|
||||||
} break;
|
|
||||||
case EntryContext::EntryOuter: {
|
|
||||||
if (skipCheck(line)) {
|
|
||||||
line = stream.readLine().trimmed();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.startsWith('[')) {
|
|
||||||
auto group = parseGroupHeader(line);
|
|
||||||
|
|
||||||
if (group == m_entryMap.end()) {
|
|
||||||
return DesktopErrorCode::InvalidFormat;
|
|
||||||
}
|
|
||||||
currentGroup = group;
|
|
||||||
bool isMainEntry = (currentGroup.key() == DesktopFileEntryKey);
|
|
||||||
|
|
||||||
if ((!mainEntryParsed and isMainEntry) or (mainEntryParsed and !isMainEntry)) {
|
|
||||||
m_context = EntryContext::Entry;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qWarning() << "groupName format error:" << line;
|
|
||||||
err = DesktopErrorCode::InvalidFormat;
|
|
||||||
return err;
|
|
||||||
} break;
|
|
||||||
case EntryContext::Entry: {
|
|
||||||
line = stream.readLine().trimmed();
|
|
||||||
|
|
||||||
if (skipCheck(line)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.startsWith('[')) {
|
|
||||||
m_context = EntryContext::EntryOuter;
|
|
||||||
|
|
||||||
if (currentGroup.key() == DesktopFileEntryKey) {
|
|
||||||
mainEntryParsed = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = parseEntry(line, currentGroup);
|
|
||||||
if (err != DesktopErrorCode::NoError) {
|
|
||||||
qWarning() << "Entry format error:" << line;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case EntryContext::Done:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_context = EntryContext::Done;
|
|
||||||
if (!checkMainEntryValidation()) {
|
if (!checkMainEntryValidation()) {
|
||||||
qWarning() << "invalid MainEntry, abort.";
|
qWarning() << "invalid MainEntry, abort.";
|
||||||
err = DesktopErrorCode::MissingInfo;
|
err = DesktopErrorCode::MissingInfo;
|
||||||
@ -460,33 +445,33 @@ QString DesktopEntry::Value::unescape(const QString &str) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (str.at(i + 1).toLatin1()) {
|
switch (str.at(i + 1).toLatin1()) {
|
||||||
default:
|
default:
|
||||||
unescapedStr.append(c);
|
unescapedStr.append(c);
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
unescapedStr.append('\n');
|
unescapedStr.append('\n');
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
unescapedStr.append('\t');
|
unescapedStr.append('\t');
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
unescapedStr.append('\r');
|
unescapedStr.append('\r');
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
case '\\':
|
case '\\':
|
||||||
unescapedStr.append('\\');
|
unescapedStr.append('\\');
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
case ';':
|
case ';':
|
||||||
unescapedStr.append(';');
|
unescapedStr.append(';');
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
unescapedStr.append(' ');
|
unescapedStr.append(' ');
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,34 +547,33 @@ QDebug operator<<(QDebug debug, const DesktopErrorCode &v)
|
|||||||
QDebugStateSaver saver{debug};
|
QDebugStateSaver saver{debug};
|
||||||
QString errMsg;
|
QString errMsg;
|
||||||
switch (v) {
|
switch (v) {
|
||||||
case DesktopErrorCode::NoError: {
|
case DesktopErrorCode::NoError: {
|
||||||
errMsg = "no error.";
|
errMsg = "no error.";
|
||||||
break;
|
} break;
|
||||||
}
|
case DesktopErrorCode::NotFound: {
|
||||||
case DesktopErrorCode::NotFound: {
|
errMsg = "file not found.";
|
||||||
errMsg = "file not found.";
|
} break;
|
||||||
break;
|
case DesktopErrorCode::MismatchedFile: {
|
||||||
}
|
errMsg = "file type is mismatched.";
|
||||||
case DesktopErrorCode::MismatchedFile: {
|
} break;
|
||||||
errMsg = "file type is mismatched.";
|
case DesktopErrorCode::InvalidLocation: {
|
||||||
break;
|
errMsg = "file location is invalid, please check $XDG_DATA_DIRS.";
|
||||||
}
|
} break;
|
||||||
case DesktopErrorCode::InvalidLocation: {
|
case DesktopErrorCode::OpenFailed: {
|
||||||
errMsg = "file location is invalid, please check $XDG_DATA_DIRS.";
|
errMsg = "couldn't open the file.";
|
||||||
break;
|
} break;
|
||||||
}
|
case DesktopErrorCode::InvalidFormat: {
|
||||||
case DesktopErrorCode::OpenFailed: {
|
errMsg = "the format of desktopEntry file is invalid.";
|
||||||
errMsg = "couldn't open the file.";
|
} break;
|
||||||
break;
|
case DesktopErrorCode::MissingInfo: {
|
||||||
}
|
errMsg = "missing required infomation.";
|
||||||
case DesktopErrorCode::InvalidFormat: {
|
} break;
|
||||||
errMsg = "the format of desktopEntry file is invalid.";
|
case DesktopErrorCode::Parsed: {
|
||||||
break;
|
errMsg = "this desktop entry is parsed.";
|
||||||
}
|
} break;
|
||||||
case DesktopErrorCode::MissingInfo: {
|
case DesktopErrorCode::InternalError: {
|
||||||
errMsg = "missing required infomation.";
|
errMsg = "internal error of parser.";
|
||||||
break;
|
} break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
debug << errMsg;
|
debug << errMsg;
|
||||||
return debug;
|
return debug;
|
||||||
|
@ -15,7 +15,17 @@
|
|||||||
|
|
||||||
constexpr static auto defaultKeyStr = "default";
|
constexpr static auto defaultKeyStr = "default";
|
||||||
|
|
||||||
enum class DesktopErrorCode { NoError, NotFound, MismatchedFile, InvalidLocation, InvalidFormat, OpenFailed, MissingInfo };
|
enum class DesktopErrorCode {
|
||||||
|
NoError,
|
||||||
|
NotFound,
|
||||||
|
MismatchedFile,
|
||||||
|
InvalidLocation,
|
||||||
|
InvalidFormat,
|
||||||
|
OpenFailed,
|
||||||
|
MissingInfo,
|
||||||
|
Parsed,
|
||||||
|
InternalError,
|
||||||
|
};
|
||||||
|
|
||||||
enum class EntryContext { Unknown, EntryOuter, Entry, Done };
|
enum class EntryContext { Unknown, EntryOuter, Entry, Done };
|
||||||
|
|
||||||
@ -124,18 +134,11 @@ public:
|
|||||||
[[nodiscard]] DesktopErrorCode parse(QTextStream &stream) noexcept;
|
[[nodiscard]] DesktopErrorCode parse(QTextStream &stream) noexcept;
|
||||||
[[nodiscard]] std::optional<QMap<QString, Value>> group(const QString &key) const noexcept;
|
[[nodiscard]] std::optional<QMap<QString, Value>> group(const QString &key) const noexcept;
|
||||||
[[nodiscard]] std::optional<Value> value(const QString &key, const QString &valueKey) const noexcept;
|
[[nodiscard]] std::optional<Value> value(const QString &key, const QString &valueKey) const noexcept;
|
||||||
static bool isInvalidLocaleString(const QString &str) noexcept;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EntryContext m_context{EntryContext::EntryOuter};
|
|
||||||
QMap<QString, QMap<QString, Value>> m_entryMap;
|
|
||||||
|
|
||||||
auto parseGroupHeader(const QString &str) noexcept;
|
|
||||||
[[nodiscard]] bool checkMainEntryValidation() const noexcept;
|
[[nodiscard]] bool checkMainEntryValidation() const noexcept;
|
||||||
static bool skipCheck(const QString &line) noexcept;
|
QMap<QString, QMap<QString, Value>> m_entryMap;
|
||||||
static DesktopErrorCode parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept;
|
bool m_parsed{false};
|
||||||
static QPair<QString, QString> processEntry(const QString &str) noexcept;
|
|
||||||
static std::optional<QPair<QString, QString>> processEntryKey(const QString &keyStr) noexcept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const DesktopEntry::Value &v);
|
QDebug operator<<(QDebug debug, const DesktopEntry::Value &v);
|
||||||
|
36
src/global.h
36
src/global.h
@ -117,26 +117,26 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (m_serverType) {
|
switch (m_serverType) {
|
||||||
case DBusType::Session:
|
case DBusType::Session:
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case DBusType::System: {
|
case DBusType::System: {
|
||||||
m_serverConnection.emplace(QDBusConnection::connectToBus(static_cast<QDBusConnection::BusType>(m_serverType),
|
m_serverConnection.emplace(QDBusConnection::connectToBus(static_cast<QDBusConnection::BusType>(m_serverType),
|
||||||
ApplicationManagerServerDBusName));
|
ApplicationManagerServerDBusName));
|
||||||
if (!m_serverConnection->isConnected()) {
|
if (!m_serverConnection->isConnected()) {
|
||||||
qFatal("%s", m_serverConnection->lastError().message().toLocal8Bit().data());
|
qFatal("%s", m_serverConnection->lastError().message().toLocal8Bit().data());
|
||||||
}
|
|
||||||
return m_serverConnection.value();
|
|
||||||
}
|
}
|
||||||
case DBusType::Custom: {
|
return m_serverConnection.value();
|
||||||
if (m_serverBusAddress.isEmpty()) {
|
}
|
||||||
qFatal("connect to custom dbus must init this object by custom dbus address");
|
case DBusType::Custom: {
|
||||||
}
|
if (m_serverBusAddress.isEmpty()) {
|
||||||
m_serverConnection.emplace(QDBusConnection::connectToBus(m_serverBusAddress, ApplicationManagerServerDBusName));
|
qFatal("connect to custom dbus must init this object by custom dbus address");
|
||||||
if (!m_serverConnection->isConnected()) {
|
|
||||||
qFatal("%s", m_serverConnection->lastError().message().toLocal8Bit().data());
|
|
||||||
}
|
|
||||||
return m_serverConnection.value();
|
|
||||||
}
|
}
|
||||||
|
m_serverConnection.emplace(QDBusConnection::connectToBus(m_serverBusAddress, ApplicationManagerServerDBusName));
|
||||||
|
if (!m_serverConnection->isConnected()) {
|
||||||
|
qFatal("%s", m_serverConnection->lastError().message().toLocal8Bit().data());
|
||||||
|
}
|
||||||
|
return m_serverConnection.value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
|
Loading…
Reference in New Issue
Block a user