svg_loader XMLParser: Clean up parameter type
[platform/core/graphics/tizenvg.git] / src / loaders / svg / tvgXmlParser.cpp
1 /*
2  * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
3
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22
23 #include <ctype.h>
24 #include <string>
25
26 #ifdef _WIN32
27     #include <malloc.h>
28 #else
29     #include <alloca.h>
30 #endif
31
32 #include "tvgXmlParser.h"
33
34 /************************************************************************/
35 /* Internal Class Implementation                                        */
36 /************************************************************************/
37
38 bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
39 {
40 #ifdef THORVG_LOG_ENABLED
41     const auto attributesNum = 6;
42     const struct
43     {
44         const char* tag;
45         bool tagWildcard; //If true, it is assumed that a wildcard is used after the tag. (ex: tagName*)
46         const char* value;
47     } attributes[] = {
48         {"id", false, nullptr},
49         {"data-name", false, nullptr},
50         {"overflow", false, "visible"},
51         {"version", false, nullptr},
52         {"xmlns", true, nullptr},
53         {"xml:space", false, nullptr},
54     };
55
56     for (unsigned int i = 0; i < attributesNum; ++i) {
57         if (!strncmp(tagAttribute, attributes[i].tag, attributes[i].tagWildcard ? strlen(attributes[i].tag) : strlen(tagAttribute))) {
58             if (attributes[i].value && tagValue) {
59                 if (!strncmp(tagValue, attributes[i].value, strlen(tagValue))) {
60                     return true;
61                 } else continue;
62             }
63             return true;
64         }
65     }
66     return false;
67 #endif
68     return true;
69 }
70
71
72 static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
73 {
74     for (; itr < itrEnd; itr++) {
75         if (isspace((unsigned char)*itr)) break;
76     }
77     return itr;
78 }
79
80
81 static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
82 {
83     for (; itr < itrEnd; itr++) {
84         if (!isspace((unsigned char)*itr)) break;
85     }
86     return itr;
87 }
88
89
90 static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
91 {
92     for (itr--; itr > itrStart; itr--) {
93         if (!isspace((unsigned char)*itr)) break;
94     }
95     return itr + 1;
96 }
97
98
99 static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
100 {
101     auto p = itr;
102     while (itr < itrEnd && *itr == '&') {
103         for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
104             if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) {
105                 itr += xmlEntityLength[i];
106                 break;
107             }
108         }
109         if (itr == p) break;
110         p = itr;
111     }
112     return itr;
113 }
114
115
116 static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
117 {
118     auto p = itr;
119     while (itr > itrStart && *(itr - 1) == ';') {
120         for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
121             if (itr - xmlEntityLength[i] > itrStart &&
122                 strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) {
123                 itr -= xmlEntityLength[i];
124                 break;
125             }
126         }
127         if (itr == p) break;
128         p = itr;
129     }
130     return itr;
131 }
132
133
134 static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd)
135 {
136     itr = _simpleXmlSkipWhiteSpace(itr, itrEnd);
137     auto p = itr;
138     while (true) {
139         if (p != (itr = _simpleXmlSkipXmlEntities(itr, itrEnd))) p = itr;
140         else break;
141         if (p != (itr = _simpleXmlSkipWhiteSpace(itr, itrEnd))) p = itr;
142         else break;
143     }
144     return itr;
145 }
146
147
148 static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart)
149 {
150     itr = _simpleXmlUnskipWhiteSpace(itr, itrStart);
151     auto p = itr;
152     while (true) {
153         if (p != (itr = _simpleXmlUnskipXmlEntities(itr, itrStart))) p = itr;
154         else break;
155         if (p != (itr = _simpleXmlUnskipWhiteSpace(itr, itrStart))) p = itr;
156         else break;
157     }
158     return itr;
159 }
160
161
162 static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
163 {
164     return (const char*)memchr(itr, '<', itrEnd - itr);
165 }
166
167
168 static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
169 {
170     bool insideQuote = false;
171     for (; itr < itrEnd; itr++) {
172         if (*itr == '"') insideQuote = !insideQuote;
173         if (!insideQuote) {
174             if ((*itr == '>') || (*itr == '<'))
175                 return itr;
176         }
177     }
178     return nullptr;
179 }
180
181
182 static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
183 {
184     for (; itr < itrEnd; itr++) {
185         if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
186     }
187     return nullptr;
188 }
189
190
191 static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
192 {
193     for (; itr < itrEnd; itr++) {
194         if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
195     }
196     return nullptr;
197 }
198
199
200 static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
201 {
202     for (; itr < itrEnd; itr++) {
203         if (*itr == '>') return itr;
204     }
205     return nullptr;
206 }
207
208
209 static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
210 {
211     toff = 0;
212     if (itr[1] == '/') {
213         toff = 1;
214         return SimpleXMLType::Close;
215     } else if (itr[1] == '?') {
216         toff = 1;
217         return SimpleXMLType::Processing;
218     } else if (itr[1] == '!') {
219         if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
220             toff = sizeof("!DOCTYPE") - 1;
221             return SimpleXMLType::Doctype;
222         } else if (itr + sizeof("<!>") - 1 < itrEnd) {
223             toff = sizeof("!") - 1;
224             return SimpleXMLType::DoctypeChild;
225         } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
226             toff = sizeof("![CDATA[") - 1;
227             return SimpleXMLType::CData;
228         } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
229             toff = sizeof("!--") - 1;
230             return SimpleXMLType::Comment;
231         }
232         return SimpleXMLType::Open;
233     }
234     return SimpleXMLType::Open;
235 }
236
237
238 /************************************************************************/
239 /* External Class Implementation                                        */
240 /************************************************************************/
241
242 const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
243 {
244 #ifdef THORVG_LOG_ENABLED
245     static const char* TYPE_NAMES[] = {
246         "Svg",
247         "G",
248         "Defs",
249         "Animation",
250         "Arc",
251         "Circle",
252         "Ellipse",
253         "Image",
254         "Line",
255         "Path",
256         "Polygon",
257         "Polyline",
258         "Rect",
259         "Text",
260         "TextArea",
261         "Tspan",
262         "Use",
263         "Video",
264         "ClipPath",
265         "Mask",
266         "Unknown",
267     };
268     return TYPE_NAMES[(int) type];
269 #endif
270     return nullptr;
271 }
272
273
274 bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
275 {
276 #ifdef THORVG_LOG_ENABLED
277     const auto elementsNum = 1;
278     const char* const elements[] = { "title" };
279
280     for (unsigned int i = 0; i < elementsNum; ++i) {
281         if (!strncmp(tagName, elements[i], strlen(tagName))) {
282             return true;
283         }
284     }
285     return false;
286 #else
287     return true;
288 #endif
289 }
290
291
292 bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
293 {
294     const char *itr = buf, *itrEnd = buf + bufLength;
295     char* tmpBuf = (char*)alloca(bufLength + 1);
296
297     if (!buf || !func) return false;
298
299     while (itr < itrEnd) {
300         const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd);
301         const char *key, *keyEnd, *value, *valueEnd;
302         char* tval;
303
304         if (p == itrEnd) return true;
305
306         key = p;
307         for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
308             if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
309         }
310         if (keyEnd == itrEnd) return false;
311         if (keyEnd == key) continue;
312
313         if (*keyEnd == '=') value = keyEnd + 1;
314         else {
315             value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
316             if (!value) return false;
317             value++;
318         }
319         keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
320
321         value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
322         if (value == itrEnd) return false;
323
324         if ((*value == '"') || (*value == '\'')) {
325             valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
326             if (!valueEnd) return false;
327             value++;
328         } else {
329             valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
330         }
331
332         itr = valueEnd + 1;
333
334         value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
335         valueEnd = _unskipWhiteSpacesAndXmlEntities(valueEnd, value);
336
337         memcpy(tmpBuf, key, keyEnd - key);
338         tmpBuf[keyEnd - key] = '\0';
339
340         tval = tmpBuf + (keyEnd - key) + 1;
341         int i = 0;
342         while (value < valueEnd) {
343             value = _simpleXmlSkipXmlEntities(value, valueEnd);
344             tval[i++] = *value;
345             value++;
346         }
347         tval[i] = '\0';
348
349         if (!func((void*)data, tmpBuf, tval)) {
350             if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) {
351                 TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id->c_str() : "NO_ID", tmpBuf, tval ? tval : "NONE");
352             }
353         }
354     }
355     return true;
356 }
357
358
359 bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
360 {
361     const char *itr = buf, *itrEnd = buf + bufLength;
362
363     if (!buf || !func) return false;
364
365     while (itr < itrEnd) {
366         if (itr[0] == '<') {
367             //Invalid case
368             if (itr + 1 >= itrEnd) return false;
369
370             size_t toff = 0;
371             SimpleXMLType type = _getXMLType(itr, itrEnd, toff);
372
373             const char* p;
374             if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
375             else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
376             else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
377             else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
378
379             if (p) {
380                 //Invalid case: '<' nested
381                 if (*p == '<') return false;
382                 const char *start, *end;
383
384                 start = itr + 1 + toff;
385                 end = p;
386
387                 switch (type) {
388                     case SimpleXMLType::Open: {
389                         if (p[-1] == '/') {
390                             type = SimpleXMLType::OpenEmpty;
391                             end--;
392                         }
393                         break;
394                     }
395                     case SimpleXMLType::CData: {
396                         if (!memcmp(p - 2, "]]", 2)) end -= 2;
397                         break;
398                     }
399                     case SimpleXMLType::Processing: {
400                         if (p[-1] == '?') end--;
401                         break;
402                     }
403                     case SimpleXMLType::Comment: {
404                         if (!memcmp(p - 2, "--", 2)) end -= 2;
405                         break;
406                     }
407                     default: {
408                         break;
409                     }
410                 }
411
412                 if (strip && (type != SimpleXMLType::CData)) {
413                     start = _skipWhiteSpacesAndXmlEntities(start, end);
414                     end = _unskipWhiteSpacesAndXmlEntities(end, start);
415                 }
416
417                 if (!func((void*)data, type, start, (unsigned int)(end - start))) return false;
418
419                 itr = p + 1;
420             } else {
421                 return false;
422             }
423         } else {
424             const char *p, *end;
425
426             if (strip) {
427                 p = itr;
428                 p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
429                 if (p) {
430                     if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
431                     itr = p;
432                 }
433             }
434
435             p = _simpleXmlFindStartTag(itr, itrEnd);
436             if (!p) p = itrEnd;
437
438             end = p;
439             if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
440
441             if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (size_t)(end - itr))) return false;
442
443             if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (size_t)(p - end))) return false;
444
445             itr = p;
446         }
447     }
448     return true;
449 }
450
451
452 bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
453 {
454     const char* end;
455     char* key;
456     char* val;
457     char* next;
458
459     if (!buf) return false;
460
461     end = buf + strlen(buf);
462     key = (char*)alloca(end - buf + 1);
463     val = (char*)alloca(end - buf + 1);
464
465     if (buf == end) return true;
466
467     do {
468         char* sep = (char*)strchr(buf, ':');
469         next = (char*)strchr(buf, ';');
470
471         key[0] = '\0';
472         val[0] = '\0';
473
474         if (next == nullptr && sep != nullptr) {
475             memcpy(key, buf, sep - buf);
476             key[sep - buf] = '\0';
477
478             memcpy(val, sep + 1, end - sep - 1);
479             val[end - sep - 1] = '\0';
480         } else if (sep < next && sep != nullptr) {
481             memcpy(key, buf, sep - buf);
482             key[sep - buf] = '\0';
483
484             memcpy(val, sep + 1, next - sep - 1);
485             val[next - sep - 1] = '\0';
486         } else if (next) {
487             memcpy(key, buf, next - buf);
488             key[next - buf] = '\0';
489         }
490
491         if (key[0]) {
492             key = const_cast<char*>(_simpleXmlSkipWhiteSpace(key, key + strlen(key)));
493             key[_simpleXmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0';
494             val = const_cast<char*>(_simpleXmlSkipWhiteSpace(val, val + strlen(val)));
495             val[_simpleXmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0';
496
497             if (!func((void*)data, key, val)) {
498                 if (!_isIgnoreUnsupportedLogAttributes(key, val)) {
499                     TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id->c_str() : "NO_ID", key, val ? val : "NONE");
500                 }
501             }
502         }
503
504         buf = next + 1;
505     } while (next != nullptr);
506
507     return true;
508 }
509
510
511 const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
512 {
513     const char *itr = buf, *itrEnd = buf + bufLength;
514
515     for (; itr < itrEnd; itr++) {
516         if (!isspace((unsigned char)*itr)) {
517             //User skip tagname and already gave it the attributes.
518             if (*itr == '=') return buf;
519         } else {
520             itr = _simpleXmlUnskipXmlEntities(itr, buf);
521             if (itr == itrEnd) return nullptr;
522             return itr;
523         }
524     }
525
526     return nullptr;
527 }