File indexing completed on 2024-11-17 04:17:27

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "mapcssparser.h"
0008 #include "mapcssparser_p.h"
0009 #include "logging.h"
0010 
0011 #include "mapcssparser_impl.h"
0012 #include "mapcssdeclaration_p.h"
0013 #include "mapcssrule_p.h"
0014 #include "mapcssscanner.h"
0015 #include "mapcssstyle.h"
0016 #include "mapcssstyle_p.h"
0017 
0018 #include <QDebug>
0019 #include <QFile>
0020 #include <QFileInfo>
0021 #include <QScopeGuard>
0022 
0023 #include <cstring>
0024 
0025 char* unquoteString(const char *str)
0026 {
0027     const auto size = strlen(str) - 2;
0028     if (size <= 0) {
0029         return nullptr;
0030     }
0031     auto out = (char*)malloc(size + 1);
0032     memset(out, 0, size + 1);
0033     auto outIt = out;
0034     for (auto it = str + 1; it < str + size + 1; ++it, ++outIt) {
0035         if (*it == '\\') {
0036             ++it;
0037             switch (*it) {
0038                 case '\\':
0039                 case '"':
0040                     *outIt = *it; break;
0041                 case 'n':
0042                     *outIt = '\n'; break;
0043                 case 't':
0044                     *outIt = '\t'; break;
0045                 default:
0046                     *outIt++ = '\\';
0047                     *outIt = *it;
0048             }
0049         } else {
0050             *outIt = *it;
0051         }
0052     }
0053     return out;
0054 }
0055 
0056 using namespace KOSMIndoorMap;
0057 
0058 MapCSSParser::MapCSSParser()
0059     : d(new MapCSSParserPrivate)
0060 {
0061 }
0062 
0063 MapCSSParser::~MapCSSParser() = default;
0064 
0065 bool MapCSSParser::hasError() const
0066 {
0067     return d->m_error;
0068 }
0069 
0070 QString MapCSSParser::fileName() const
0071 {
0072     return d->m_currentFileName;
0073 }
0074 
0075 QString MapCSSParser::errorMessage() const
0076 {
0077     if (!d->m_error) {
0078         return {};
0079     }
0080 
0081     return d->m_errorMsg + QLatin1String(": ") + fileName() + QLatin1Char(':') + QString::number(d->m_line) + QLatin1Char(':') + QString::number(d->m_column);
0082 }
0083 
0084 MapCSSStyle MapCSSParser::parse(const QString &fileName)
0085 {
0086     d->m_error = true;
0087 
0088     MapCSSStyle style;
0089     d->parse(&style, fileName, {});
0090     if (d->m_error) {
0091         return MapCSSStyle();
0092     }
0093 
0094     return style;
0095 }
0096 
0097 void MapCSSParserPrivate::parse(MapCSSStyle *style, const QString &fileName, ClassSelectorKey importClass)
0098 {
0099     QFile f(fileName);
0100     if (!f.open(QFile::ReadOnly)) {
0101         qCWarning(Log) << f.fileName() << f.errorString();
0102         m_error = true;
0103         m_errorMsg = f.errorString();
0104         return;
0105     }
0106     m_currentFileName = fileName;
0107     m_currentStyle = style;
0108     m_importClass = importClass;
0109 
0110     yyscan_t scanner;
0111     if (yylex_init(&scanner)) {
0112         return;
0113     }
0114     const auto lexerCleanup = qScopeGuard([&scanner]{ yylex_destroy(scanner); });
0115 
0116     const auto b = f.readAll();
0117     YY_BUFFER_STATE state;
0118     state = yy_scan_string(b.constData(), scanner);
0119     if (yyparse(this, scanner)) {
0120         m_error = true;
0121         return;
0122     }
0123 
0124     yy_delete_buffer(state, scanner);
0125 
0126     m_error = false;
0127     m_currentStyle = nullptr;
0128     m_importClass = {};
0129 }
0130 
0131 bool MapCSSParserPrivate::addImport(char* fileName, ClassSelectorKey importClass)
0132 {
0133     auto cssFile = QString::fromUtf8(fileName);
0134     free(fileName);
0135 
0136     if (QFileInfo(cssFile).isRelative()) {
0137         cssFile = QFileInfo(m_currentFileName).absolutePath() + QLatin1Char('/') + cssFile;
0138     }
0139 
0140     MapCSSParser p;
0141     p.d->parse(m_currentStyle, cssFile, importClass);
0142     if (p.hasError()) {
0143         m_error = p.d->m_error;
0144         m_errorMsg = p.errorMessage();
0145     }
0146     return !p.hasError();
0147 }
0148 
0149 void MapCSSParserPrivate::addRule(MapCSSRule *rule)
0150 {
0151     if (!m_importClass.isNull()) {
0152         auto decl = new MapCSSDeclaration(MapCSSDeclaration::ClassDeclaration);
0153         decl->setClassSelectorKey(m_importClass);
0154         rule->addDeclaration(decl);
0155     }
0156     MapCSSStylePrivate::get(m_currentStyle)->m_rules.push_back(std::unique_ptr<MapCSSRule>(rule));
0157 }
0158 
0159 void MapCSSParserPrivate::setError(const QString &msg, int line, int column)
0160 {
0161     m_error = true;
0162     m_errorMsg = msg;
0163     m_line = line;
0164     m_column = column;
0165 }
0166 
0167 ClassSelectorKey MapCSSParserPrivate::makeClassSelector(const char *str, std::size_t len) const
0168 {
0169     return MapCSSStylePrivate::get(m_currentStyle)->m_classSelectorRegistry.makeKey(str, len, OSM::StringMemory::Transient);
0170 }
0171 
0172 LayerSelectorKey MapCSSParserPrivate::makeLayerSelector(const char *str, std::size_t len) const
0173 {
0174     if (!str || std::strcmp(str, "default") == 0) {
0175         return {};
0176     }
0177     return MapCSSStylePrivate::get(m_currentStyle)->m_layerSelectorRegistry.makeKey(str, len, OSM::StringMemory::Transient);
0178 }