1 /*
2     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "mapcssparser.h"
8 #include "logging.h"
9 
10 #include "mapcssparser_p.h"
11 #include "mapcssrule_p.h"
12 #include "mapcssscanner.h"
13 #include "mapcssstyle.h"
14 #include "mapcssstyle_p.h"
15 
16 #include <QDebug>
17 #include <QFile>
18 #include <QFileInfo>
19 #include <QScopeGuard>
20 
21 #include <cstring>
22 
unquoteString(const char * str)23 char* unquoteString(const char *str)
24 {
25     const auto size = strlen(str) - 2;
26     if (size <= 0)
27         return nullptr;
28     auto out = (char*)malloc(size + 1);
29     memset(out, 0, size + 1);
30     auto outIt = out;
31     for (auto it = str + 1; it < str + size + 1; ++it, ++outIt) {
32         if (*it == '\\') {
33             ++it;
34             switch (*it) {
35                 case '\\':
36                 case '"':
37                     *outIt = *it; break;
38                 case 'n':
39                     *outIt = '\n'; break;
40                 case 't':
41                     *outIt = '\t'; break;
42                 default:
43                     *outIt++ = '\\';
44                     *outIt = *it;
45             }
46         } else {
47             *outIt = *it;
48         }
49     }
50     return out;
51 }
52 
53 using namespace KOSMIndoorMap;
54 
hasError() const55 bool MapCSSParser::hasError() const
56 {
57     return m_error;
58 }
59 
fileName() const60 QString MapCSSParser::fileName() const
61 {
62     return m_currentFileName;
63 }
64 
errorMessage() const65 QString MapCSSParser::errorMessage() const
66 {
67     if (!m_error) {
68         return {};
69     }
70 
71     return m_errorMsg + QLatin1String(": ") + fileName() + QLatin1Char(':') + QString::number(m_line) + QLatin1Char(':') + QString::number(m_column);
72 }
73 
parse(const QString & fileName)74 MapCSSStyle MapCSSParser::parse(const QString &fileName)
75 {
76     m_error = true;
77 
78     MapCSSStyle style;
79     parse(&style, fileName);
80     if (m_error) {
81         return MapCSSStyle();
82     }
83 
84     return style;
85 }
86 
parse(MapCSSStyle * style,const QString & fileName)87 void MapCSSParser::parse(MapCSSStyle *style, const QString &fileName)
88 {
89     QFile f(fileName);
90     if (!f.open(QFile::ReadOnly)) {
91         qCWarning(Log) << f.fileName() << f.errorString();
92         m_error = true;
93         m_errorMsg = f.errorString();
94         return;
95     }
96     m_currentFileName = fileName;
97     m_currentStyle = style;
98 
99     yyscan_t scanner;
100     if (yylex_init(&scanner)) {
101         return;
102     }
103     const auto lexerCleanup = qScopeGuard([&scanner]{ yylex_destroy(scanner); });
104 
105     const auto b = f.readAll();
106     YY_BUFFER_STATE state;
107     state = yy_scan_string(b.constData(), scanner);
108     if (yyparse(this, scanner)) {
109         m_error = true;
110         return;
111     }
112 
113     yy_delete_buffer(state, scanner);
114 
115     m_error = false;
116     m_currentStyle = nullptr;
117 }
118 
addImport(char * fileName)119 bool MapCSSParser::addImport(char* fileName)
120 {
121     auto cssFile = QString::fromUtf8(fileName);
122     free(fileName);
123 
124     if (QFileInfo(cssFile).isRelative()) {
125         cssFile = QFileInfo(m_currentFileName).absolutePath() + QLatin1Char('/') + cssFile;
126     }
127 
128     MapCSSParser p;
129     p.parse(m_currentStyle, cssFile);
130     if (p.hasError()) {
131         m_error = p.m_error;
132         m_errorMsg = p.errorMessage();
133     }
134     return !p.hasError();
135 }
136 
addRule(MapCSSRule * rule)137 void MapCSSParser::addRule(MapCSSRule *rule)
138 {
139     MapCSSStylePrivate::get(m_currentStyle)->m_rules.push_back(std::unique_ptr<MapCSSRule>(rule));
140 }
141 
setError(const QString & msg,int line,int column)142 void MapCSSParser::setError(const QString &msg, int line, int column)
143 {
144     m_error = true;
145     m_errorMsg = msg;
146     m_line = line;
147     m_column = column;
148 }
149 
makeClassSelector(const char * str,std::size_t len)150 ClassSelectorKey MapCSSParser::makeClassSelector(const char *str, std::size_t len)
151 {
152     return MapCSSStylePrivate::get(m_currentStyle)->m_classSelectorRegistry.makeKey(str, len, OSM::StringMemory::Transient);
153 }
154 
makeLayerSelector(const char * str,std::size_t len)155 LayerSelectorKey MapCSSParser::makeLayerSelector(const char *str, std::size_t len)
156 {
157     if (!str || std::strcmp(str, "default") == 0) {
158         return {};
159     }
160     return MapCSSStylePrivate::get(m_currentStyle)->m_layerSelectorRegistry.makeKey(str, len, OSM::StringMemory::Transient);
161 }
162