File indexing completed on 2024-05-12 04:01:32
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de> 0004 SPDX-License-Identifier: MIT 0005 */ 0006 0007 #include "mecard.h" 0008 0009 #include <vector> 0010 0011 using namespace Prison; 0012 0013 class Prison::MeCardPrivate 0014 { 0015 public: 0016 QStringView header; 0017 struct Element { 0018 QStringView key; 0019 QStringList values; 0020 bool operator<(QStringView other) const; 0021 }; 0022 std::vector<Element> elements; 0023 }; 0024 0025 bool MeCardPrivate::Element::operator<(QStringView other) const 0026 { 0027 return key < other; 0028 } 0029 0030 MeCard::MeCard() 0031 : d(new MeCardPrivate()) 0032 { 0033 } 0034 0035 MeCard::MeCard(MeCard &&other) noexcept 0036 : d(std::move(other.d)) 0037 { 0038 other.d.reset(); 0039 } 0040 0041 MeCard &MeCard::operator=(MeCard &&other) noexcept 0042 { 0043 std::swap(d, other.d); 0044 return *this; 0045 } 0046 0047 MeCard::~MeCard() = default; 0048 0049 std::optional<MeCard> MeCard::parse(const QString &data) 0050 { 0051 const auto idx = data.indexOf(QLatin1Char(':')); 0052 if (idx <= 0 || idx >= data.size() - 1) { 0053 return std::nullopt; 0054 } 0055 0056 MeCard m; 0057 m.d->header = QStringView(data).left(idx); 0058 0059 auto remaining = QStringView(data).mid(idx + 1); 0060 while (remaining.size() > 0) { 0061 const auto keyIdx = remaining.indexOf(QLatin1Char(':')); 0062 if (keyIdx <= 0 || keyIdx + 2 >= remaining.size()) { 0063 break; 0064 } 0065 0066 QString value; 0067 auto elemIdx = keyIdx + 1; 0068 bool inQuote = false; 0069 for (; elemIdx < remaining.size() - 1; ++elemIdx) { 0070 auto c = remaining.at(elemIdx); 0071 if (elemIdx == (keyIdx + 1) && c == QLatin1Char('"')) { // leading quote 0072 inQuote = true; 0073 continue; 0074 } 0075 if (inQuote && c == QLatin1Char('"') && remaining.at(elemIdx + 1) == QLatin1Char(';')) { // trailing quote 0076 break; 0077 } 0078 if (c == QLatin1Char(';')) { // end of element 0079 break; 0080 } 0081 if (c == QLatin1Char('\\')) { // quoted character 0082 ++elemIdx; 0083 c = remaining.at(elemIdx); 0084 } 0085 value.push_back(c); 0086 } 0087 0088 const auto key = remaining.left(keyIdx); 0089 auto it = std::lower_bound(m.d->elements.begin(), m.d->elements.end(), key); 0090 if (it == m.d->elements.end()) { 0091 m.d->elements.push_back(MeCardPrivate::Element()); 0092 it = std::prev(m.d->elements.end()); 0093 } else if ((*it).key != key) { 0094 it = m.d->elements.insert(it, MeCardPrivate::Element()); 0095 } 0096 (*it).key = key; 0097 (*it).values.push_back(value); 0098 0099 remaining = remaining.mid(elemIdx + 1); 0100 } 0101 0102 if (m.d->elements.empty()) { 0103 return std::nullopt; 0104 } 0105 0106 return m; 0107 } 0108 0109 QString MeCard::header() const 0110 { 0111 return d->header.toString(); 0112 } 0113 0114 QStringView MeCard::headerView() const 0115 { 0116 return d->header; 0117 } 0118 0119 QString MeCard::value(QStringView key) const 0120 { 0121 const auto it = std::lower_bound(d->elements.begin(), d->elements.end(), key); 0122 if (it != d->elements.end() && (*it).key == key && (*it).values.size() == 1) { 0123 return (*it).values.at(0); 0124 } 0125 return {}; 0126 } 0127 0128 QStringList MeCard::values(QStringView key) const 0129 { 0130 const auto it = std::lower_bound(d->elements.begin(), d->elements.end(), key); 0131 if (it != d->elements.end() && (*it).key == key) { 0132 return (*it).values; 0133 } 0134 return {}; 0135 } 0136 0137 QVariantMap MeCard::toVariantMap() const 0138 { 0139 QVariantMap map; 0140 for (const auto &element : std::as_const(d->elements)) { 0141 if (element.values.size() > 1) { 0142 map.insert(element.key.toString(), element.values); 0143 } else { 0144 map.insert(element.key.toString(), element.values.at(0)); 0145 } 0146 } 0147 return map; 0148 }