File indexing completed on 2024-12-22 04:52:50
0001 /* 0002 MorkParser.cpp - Mozilla Mork Format Parser/Reader 0003 SPDX-FileCopyrightText: 2007 <ScalingWeb.com> 0004 SPDX-FileContributor: Yuriy Soroka <ysoroka@scalingweb.com> 0005 SPDX-FileContributor: Anton Fedoruk <afedoruk@scalingweb.com> 0006 0007 SPDX-License-Identifier: BSD-3-Clause 0008 */ 0009 0010 #include "MorkParser.h" 0011 #include <QFile> 0012 #include <QIODevice> 0013 // ============================================================= 0014 // MorkParser::MorkParser 0015 #include "thunderbirdplugin_debug.h" 0016 0017 MorkParser::MorkParser(int DefaultScope) 0018 { 0019 initVars(); 0020 mDefaultScope = DefaultScope; 0021 } 0022 0023 // ============================================================= 0024 // MorkParser::open 0025 0026 bool MorkParser::open(const QString &path) 0027 { 0028 initVars(); 0029 0030 QFile MorkFile(path); 0031 0032 // Open file 0033 if (!MorkFile.exists() || !MorkFile.open(QIODevice::ReadOnly)) { 0034 mError = FailedToOpen; 0035 return false; 0036 } 0037 0038 // Check magic header 0039 QByteArray MagicHeader = MorkFile.readLine(); 0040 0041 if (!MagicHeader.contains(MorkMagicHeader)) { 0042 mError = UnsupportedVersion; 0043 return false; 0044 } 0045 0046 mMorkData = MorkFile.readAll(); 0047 MorkFile.close(); 0048 0049 // Parse mork 0050 return parse(); 0051 } 0052 0053 // ============================================================= 0054 // MorkParser::error 0055 0056 MorkErrors MorkParser::error() const 0057 { 0058 return mError; 0059 } 0060 0061 // ============================================================= 0062 // MorkParser::initVars 0063 0064 void MorkParser::initVars() 0065 { 0066 mError = NoError; 0067 mMorkPos = 0; 0068 nowParsing_ = NPValues; 0069 mCurrentCells = nullptr; 0070 mNextAddValueId = 0x7fffffff; 0071 } 0072 0073 // ============================================================= 0074 // MorkParser::parse 0075 0076 bool MorkParser::parse() 0077 { 0078 bool Result = true; 0079 char cur = 0; 0080 0081 // Run over mork chars and parse each term 0082 cur = nextChar(); 0083 0084 while (Result && cur) { 0085 if (!isWhiteSpace(cur)) { 0086 // Figure out what a term 0087 switch (cur) { 0088 case '<': 0089 // Dict 0090 Result = parseDict(); 0091 break; 0092 case '/': 0093 // Comment 0094 Result = parseComment(); 0095 break; 0096 case '{': 0097 Result = parseTable(); 0098 // Table 0099 break; 0100 case '[': 0101 Result = parseRow(0, 0); 0102 // Row 0103 break; 0104 case '@': 0105 Result = parseGroup(); 0106 // Group 0107 break; 0108 default: 0109 mError = DefectedFormat; 0110 Result = false; 0111 break; 0112 } 0113 } 0114 0115 // Get next char 0116 cur = nextChar(); 0117 } 0118 0119 return Result; 0120 } 0121 0122 // ============================================================= 0123 // MorkParser::isWhiteSpace 0124 0125 bool MorkParser::isWhiteSpace(char c) const 0126 { 0127 switch (c) { 0128 case ' ': 0129 case '\t': 0130 case '\r': 0131 case '\n': 0132 case '\f': 0133 return true; 0134 default: 0135 return false; 0136 } 0137 } 0138 0139 // ============================================================= 0140 // MorkParser::nextChar 0141 0142 char MorkParser::nextChar() 0143 { 0144 char cur = 0; 0145 0146 if (mMorkPos < mMorkData.length()) { 0147 cur = mMorkData[mMorkPos]; 0148 mMorkPos++; 0149 } 0150 0151 if (!cur) { 0152 cur = 0; 0153 } 0154 0155 return cur; 0156 } 0157 0158 // ============================================================= 0159 // MorkParser::parseDict 0160 0161 bool MorkParser::parseDict() 0162 { 0163 char cur = nextChar(); 0164 bool Result = true; 0165 nowParsing_ = NPValues; 0166 0167 while (Result && cur != '>' && cur) { 0168 if (!isWhiteSpace(cur)) { 0169 switch (cur) { 0170 case '<': 0171 if (mMorkData.mid(mMorkPos - 1, strlen(MorkDictColumnMeta)) == MorkDictColumnMeta) { 0172 nowParsing_ = NPColumns; 0173 mMorkPos += strlen(MorkDictColumnMeta) - 1; 0174 } 0175 break; 0176 case '(': 0177 Result = parseCell(); 0178 break; 0179 case '/': 0180 Result = parseComment(); 0181 break; 0182 } 0183 } 0184 0185 cur = nextChar(); 0186 } 0187 0188 return Result; 0189 } 0190 0191 // ============================================================= 0192 // MorkParser::parseComment 0193 0194 bool MorkParser::parseComment() 0195 { 0196 char cur = nextChar(); 0197 if ('/' != cur) { 0198 return false; 0199 } 0200 0201 while (cur != '\r' && cur != '\n' && cur) { 0202 cur = nextChar(); 0203 } 0204 0205 return true; 0206 } 0207 0208 // ============================================================= 0209 // MorkParser::parseCell 0210 0211 bool MorkParser::parseCell() 0212 { 0213 bool Result = true; 0214 bool bColumnOid = false; 0215 bool bValueOid = false; 0216 bool bColumn = true; 0217 int Corners = 0; 0218 0219 // Column = Value 0220 QString Column; 0221 QString Text; 0222 0223 char cur = nextChar(); 0224 0225 // Process cell start with column (bColumn == true) 0226 while (Result && cur != QLatin1Char(')') && cur) { 0227 switch (cur) { 0228 case '^': 0229 // Oids 0230 Corners++; 0231 if (Corners == 1) { 0232 bColumnOid = true; 0233 } else if (Corners == 2) { 0234 bColumn = false; 0235 bValueOid = true; 0236 } else { 0237 Text += QLatin1Char(cur); 0238 } 0239 0240 break; 0241 case '=': 0242 // From column to value 0243 if (bColumn) { 0244 bColumn = false; 0245 } else { 0246 Text += QLatin1Char(cur); 0247 } 0248 break; 0249 case '\\': { 0250 // Get next two chars 0251 char NextChar = nextChar(); 0252 if ('\r' != NextChar && '\n' != NextChar) { 0253 Text += QLatin1Char(NextChar); 0254 } else { 0255 nextChar(); 0256 } 0257 break; 0258 } 0259 case '$': { 0260 // Get next two chars 0261 QString HexChar; 0262 HexChar += QLatin1Char(nextChar()); 0263 HexChar += QLatin1Char(nextChar()); 0264 Text += QLatin1Char((char)HexChar.toInt(nullptr, 16)); 0265 break; 0266 } 0267 default: 0268 // Just a char 0269 if (bColumn) { 0270 Column += QLatin1Char(cur); 0271 } else { 0272 Text += QLatin1Char(cur); 0273 } 0274 break; 0275 } 0276 0277 cur = nextChar(); 0278 } 0279 Q_UNUSED(bColumnOid) 0280 // Apply column and text 0281 int ColumnId = Column.toInt(nullptr, 16); 0282 0283 if (NPRows != nowParsing_) { 0284 // Dicts 0285 if (!Text.isEmpty()) { 0286 if (nowParsing_ == NPColumns) { 0287 mColumns[ColumnId] = Text; 0288 qCDebug(THUNDERBIRDPLUGIN_LOG) << " column :" << ColumnId << " Text " << Text; 0289 } else { 0290 mValues[ColumnId] = Text; 0291 qCDebug(THUNDERBIRDPLUGIN_LOG) << " ColumnId " << ColumnId << " Value : " << Text; 0292 } 0293 } 0294 } else { 0295 if (!Text.isEmpty()) { 0296 // Rows 0297 int ValueId = Text.toInt(nullptr, 16); 0298 0299 if (bValueOid) { 0300 (*mCurrentCells)[ColumnId] = ValueId; 0301 } else { 0302 mNextAddValueId--; 0303 mValues[mNextAddValueId] = Text; 0304 (*mCurrentCells)[ColumnId] = mNextAddValueId; 0305 } 0306 } 0307 } 0308 return Result; 0309 } 0310 0311 // ============================================================= 0312 // MorkParser::parseTable 0313 0314 bool MorkParser::parseTable() 0315 { 0316 bool Result = true; 0317 QString TextId; 0318 int Id = 0; 0319 int Scope = 0; 0320 0321 char cur = nextChar(); 0322 0323 // Get id 0324 while (cur != QLatin1Char('{') && cur != QLatin1Char('[') && cur != QLatin1Char('}') && cur) { 0325 if (!isWhiteSpace(cur)) { 0326 TextId += QLatin1Char(cur); 0327 } 0328 0329 cur = nextChar(); 0330 } 0331 0332 parseScopeId(TextId, Id, Scope); 0333 0334 // Parse the table 0335 while (Result && cur != QLatin1Char('}') && cur) { 0336 if (!isWhiteSpace(cur)) { 0337 switch (cur) { 0338 case '{': 0339 Result = parseMeta('}'); 0340 break; 0341 case '[': 0342 Result = parseRow(Id, Scope); 0343 break; 0344 case '-': 0345 case '+': 0346 break; 0347 default: { 0348 QString JustId; 0349 while (!isWhiteSpace(cur) && cur) { 0350 JustId += QLatin1Char(cur); 0351 cur = nextChar(); 0352 0353 if (cur == QLatin1Char('}')) { 0354 return Result; 0355 } 0356 } 0357 0358 int JustIdNum = 0; 0359 int JustScopeNum = 0; 0360 parseScopeId(JustId, JustIdNum, JustScopeNum); 0361 0362 setCurrentRow(Scope, Id, JustScopeNum, JustIdNum); 0363 break; 0364 } 0365 } 0366 } 0367 0368 cur = nextChar(); 0369 } 0370 0371 return Result; 0372 } 0373 0374 // ============================================================= 0375 // MorkParser::parseScopeId 0376 0377 void MorkParser::parseScopeId(const QString &textId, int &Id, int &Scope) const 0378 { 0379 int Pos = 0; 0380 0381 if ((Pos = textId.indexOf(QLatin1Char(':'))) >= 0) { 0382 QString tId = textId.mid(0, Pos); 0383 QString tSc = textId.mid(Pos + 1, textId.length() - Pos); 0384 0385 if (tSc.length() > 1 && tSc[0] == QLatin1Char('^')) { 0386 // Delete '^' 0387 tSc.remove(0, 1); 0388 } 0389 0390 Id = tId.toInt(nullptr, 16); 0391 Scope = tSc.toInt(nullptr, 16); 0392 } else { 0393 Id = textId.toInt(nullptr, 16); 0394 } 0395 } 0396 0397 // ============================================================= 0398 // MorkParser::setCurrentRow 0399 0400 void MorkParser::setCurrentRow(int TableScope, int TableId, int RowScope, int RowId) 0401 { 0402 if (!RowScope) { 0403 RowScope = mDefaultScope; 0404 } 0405 0406 if (!TableScope) { 0407 TableScope = mDefaultScope; 0408 } 0409 0410 mCurrentCells = &(mMork[abs(TableScope)][abs(TableId)][abs(RowScope)][abs(RowId)]); 0411 } 0412 0413 // ============================================================= 0414 // MorkParser::parseRow 0415 0416 bool MorkParser::parseRow(int TableId, int TableScope) 0417 { 0418 bool Result = true; 0419 QString TextId; 0420 int Id = 0; 0421 int Scope = 0; 0422 nowParsing_ = NPRows; 0423 0424 char cur = nextChar(); 0425 0426 // Get id 0427 while (cur != QLatin1Char('(') && cur != QLatin1Char(']') && cur != QLatin1Char('[') && cur) { 0428 if (!isWhiteSpace(cur)) { 0429 TextId += QLatin1Char(cur); 0430 } 0431 0432 cur = nextChar(); 0433 } 0434 0435 parseScopeId(TextId, Id, Scope); 0436 setCurrentRow(TableScope, TableId, Scope, Id); 0437 0438 // Parse the row 0439 while (Result && cur != ']' && cur) { 0440 if (!isWhiteSpace(cur)) { 0441 switch (cur) { 0442 case '(': 0443 Result = parseCell(); 0444 break; 0445 case '[': 0446 Result = parseMeta(']'); 0447 break; 0448 default: 0449 Result = false; 0450 break; 0451 } 0452 } 0453 0454 cur = nextChar(); 0455 } 0456 0457 return Result; 0458 } 0459 0460 // ============================================================= 0461 // MorkParser::parseGroup 0462 0463 bool MorkParser::parseGroup() 0464 { 0465 return parseMeta('@'); 0466 } 0467 0468 // ============================================================= 0469 // MorkParser::parseMeta 0470 0471 bool MorkParser::parseMeta(char c) 0472 { 0473 char cur = nextChar(); 0474 0475 while (cur != c && cur) { 0476 cur = nextChar(); 0477 } 0478 0479 return true; 0480 } 0481 0482 // ============================================================= 0483 // MorkParser::getTables 0484 0485 MorkTableMap *MorkParser::getTables(int TableScope) 0486 { 0487 TableScopeMap::iterator iter; 0488 iter = mMork.find(TableScope); 0489 0490 if (iter == mMork.end()) { 0491 return nullptr; 0492 } 0493 0494 return &iter.value(); 0495 } 0496 0497 // ============================================================= 0498 // MorkParser::getRows 0499 0500 MorkRowMap *MorkParser::getRows(int RowScope, RowScopeMap *table) 0501 { 0502 RowScopeMap::iterator iter; 0503 iter = table->find(RowScope); 0504 0505 if (iter == table->end()) { 0506 return nullptr; 0507 } 0508 0509 return &iter.value(); 0510 } 0511 0512 // ============================================================= 0513 // MorkParser::getValue 0514 0515 QString MorkParser::getValue(int oid) 0516 { 0517 MorkDict::ConstIterator foundIter = mValues.constFind(oid); 0518 0519 if (foundIter == mValues.constEnd()) { 0520 return {}; 0521 } 0522 0523 return *foundIter; 0524 } 0525 0526 // ============================================================= 0527 // MorkParser::getColumn 0528 0529 QString MorkParser::getColumn(int oid) 0530 { 0531 MorkDict::ConstIterator foundIter = mColumns.constFind(oid); 0532 0533 if (foundIter == mColumns.constEnd()) { 0534 return {}; 0535 } 0536 0537 return *foundIter; 0538 }