Revision control

++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "metastrings.h"
++
++ #include <QFileInfo>
++
++ namespace {
++ using namespace Qt::Literals::StringLiterals;
++
++ static constexpr QLatin1StringView PlaceholderClass = "<class>"_L1;
++ static constexpr QLatin1StringView PlaceholderContext = "<context>"_L1;
++ static constexpr QLatin1StringView PlaceholderFile = "<file>"_L1;
++ static constexpr QLatin1StringView UnnamedPlaceholder = "<unnamed>"_L1;
++
++ static constexpr QLatin1String CppMagicComment = "TRANSLATOR"_L1;
++ } // namespace
++
++ QT_BEGIN_NAMESPACE
++
++ bool MetaStrings::parse(QString &string)
++ {
++ const QChar *ptr = string.unicode();
++ if (*ptr == extraCommentAnotation && ptr[1].isSpace()) {
++ string.remove(0, 2);
++ m_extracomment += string;
++ if (!m_extracomment.endsWith(u'\n'))
++ m_extracomment.push_back(u'\n');
++ m_extracomment.detach();
++ } else if (*ptr == idAnotation && ptr[1].isSpace()) {
++ string.remove(0, 2);
++ m_msgid = string.simplified();
++ m_msgid.detach();
++ m_error =
++ "Setting translation IDs using //= or #= is deprecated and will be removed in the upcoming versions.\n"_L1;
++ } else if (*ptr == extraAnotation && ptr[1].isSpace()) {
++ string.remove(0, 2);
++ const QString trimmed = string.trimmed();
++ int k = trimmed.indexOf(u' ');
++ if (k > -1) {
++ QString commentvalue = trimmed.mid(k + 1).trimmed();
++ if (commentvalue.startsWith(u'"') && commentvalue.endsWith(u'"')
++ && commentvalue.size() != 1) {
++ commentvalue = commentvalue.sliced(1, commentvalue.size() - 2);
++ }
++ m_extra.insert(trimmed.left(k), commentvalue);
++ }
++ } else if (*ptr == sourceTextAnotation && ptr[1].isSpace()) {
++ m_sourcetext.reserve(m_sourcetext.size() + string.size() - 2);
++ ushort *ptr = (ushort *)m_sourcetext.data() + m_sourcetext.size();
++ int p = 2, c;
++ forever {
++ if (p >= string.size())
++ break;
++ c = string.unicode()[p++].unicode();
++ if (isspace(c))
++ continue;
++ if (c != '"') {
++ m_error = "Unexpected character in meta string\n"_L1;
++ break;
++ }
++ forever {
++ if (p >= string.size()) {
++ whoops:
++ m_error = "Unterminated meta string\n"_L1;
++ break;
++ }
++ c = string.unicode()[p++].unicode();
++ if (c == '"')
++ break;
++ if (c == '\\') {
++ if (p >= string.size())
++ goto whoops;
++ c = string.unicode()[p++].unicode();
++ if (c == '\n')
++ goto whoops;
++ *ptr++ = '\\';
++ }
++ *ptr++ = c;
++ }
++ }
++ m_sourcetext.resize(ptr - (ushort *)m_sourcetext.data());
++ } else if (*ptr == labelAnotation && ptr[1].isSpace()) {
++ string.remove(0, 2);
++ m_label = string.trimmed().simplified();
++ m_label.detach();
++ } else if (const QString trimmed = string.trimmed(); trimmed.startsWith(CppMagicComment)) {
++ qsizetype idx = CppMagicComment.size();
++ QString comment =
++ QString::fromRawData(trimmed.unicode() + idx, trimmed.size() - idx).simplified();
++ if (int k = comment.indexOf(u' '); k != -1) {
++ QString context = comment.left(k);
++ comment.remove(0, k + 1);
++ m_magicComment.emplace(MagicComment{ std::move(context), std::move(comment) });
++ }
++ }
++
++ return m_error.isEmpty();
++ }
++
++ bool MetaStrings::resolveLabel(const QString &filename, const QString &context,
++ const QString &className)
++ {
++ if (m_label.contains(PlaceholderClass)) {
++ QString classStr = className;
++ if (classStr.isEmpty()) {
++ classStr = UnnamedPlaceholder;
++ m_error =
++ "Label placeholder <class> used, but no class available. Using <unnamed>.\n"_L1;
++ }
++ m_label.replace(PlaceholderClass, classStr);
++ }
++
++ if (m_label.contains(PlaceholderContext)) {
++ QString contextStr = context;
++ if (contextStr.isEmpty()) {
++ contextStr = UnnamedPlaceholder;
++ m_error +=
++ "Label placeholder <context> used, but no context available. Using <unnamed>.\n"_L1;
++ }
++ m_label.replace(PlaceholderContext, contextStr);
++ }
++
++ if (m_label.contains(PlaceholderFile))
++ m_label.replace(PlaceholderFile, QFileInfo(filename).fileName());
++
++ return m_error.isEmpty();
++ }
++
++ void MetaStrings::clear()
++ {
++ m_magicComment.reset();
++ m_extracomment.clear();
++ m_msgid.clear();
++ m_label.clear();
++ m_sourcetext.clear();
++ m_extra.clear();
++ }
++
++ bool MetaStrings::hasData() const
++ {
++ return !m_msgid.isEmpty() || m_magicComment || !m_sourcetext.isEmpty()
++ || !m_extracomment.isEmpty() || !m_extra.isEmpty() || !m_label.isEmpty();
++ }
++
++ QT_END_NAMESPACE