svg_loader: buflen arg used in the simpleXmlParseW3CAttribute()
[platform/core/graphics/tizenvg.git] / src / loaders / svg / tvgXmlParser.cpp
index 6694842..e3e42b0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -20,6 +20,7 @@
  * SOFTWARE.
  */
 
+#include <cstring>
 #include <ctype.h>
 #include <string>
 
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-#ifdef THORVG_LOG_ENABLED
-
-#include <stdio.h>
-
-string simpleXmlNodeTypeToString(SvgNodeType type)
-{
-    switch (type) {
-        case SvgNodeType::Doc: return "Svg";
-        case SvgNodeType::G: return "G";
-        case SvgNodeType::Defs: return "Defs";
-        case SvgNodeType::Animation: return "Animation";
-        case SvgNodeType::Arc: return "Arc";
-        case SvgNodeType::Circle: return "Circle";
-        case SvgNodeType::Ellipse: return "Ellipse";
-        case SvgNodeType::Image: return "Image";
-        case SvgNodeType::Line: return "Line";
-        case SvgNodeType::Path: return "Path";
-        case SvgNodeType::Polygon: return "Polygon";
-        case SvgNodeType::Polyline: return "Polyline";
-        case SvgNodeType::Rect: return "Rect";
-        case SvgNodeType::Text: return "Text";
-        case SvgNodeType::TextArea: return "TextArea";
-        case SvgNodeType::Tspan: return "Tspan";
-        case SvgNodeType::Use: return "Use";
-        case SvgNodeType::Video: return "Video";
-        case SvgNodeType::ClipPath: return "ClipPath";
-        case SvgNodeType::Mask: return "Mask";
-        default: return "Unknown";
-    }
-    return "Unknown";
-}
-
-bool isIgnoreUnsupportedLogElements(const char* tagName)
-{
-    const auto elementsNum = 1;
-    const char* const elements[] = { "title" };
-
-    for (unsigned int i = 0; i < elementsNum; ++i) {
-        if (!strncmp(tagName, elements[i], strlen(tagName))) {
-            return true;
-        }
-    }
-    return false;
-}
-
-bool _isIgnoreUnsupportedLogAttributes(const char* tagAttribute, const char* tagValue)
+bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
 {
+#ifdef THORVG_LOG_ENABLED
     const auto attributesNum = 6;
     const struct
     {
@@ -108,9 +65,10 @@ bool _isIgnoreUnsupportedLogAttributes(const char* tagAttribute, const char* tag
         }
     }
     return false;
+#endif
+    return true;
 }
 
-#endif
 
 static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
 {
@@ -142,7 +100,7 @@ static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrSt
 static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
 {
     auto p = itr;
-    while (*itr == '&' && itr < itrEnd) {
+    while (itr < itrEnd && *itr == '&') {
         for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
             if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) {
                 itr += xmlEntityLength[i];
@@ -159,7 +117,7 @@ static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd
 static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
 {
     auto p = itr;
-    while (*(itr - 1) == ';' && itr > itrStart) {
+    while (itr > itrStart && *(itr - 1) == ';') {
         for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
             if (itr - xmlEntityLength[i] > itrStart &&
                 strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) {
@@ -249,10 +207,88 @@ static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char*
 }
 
 
+static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
+{
+    toff = 0;
+    if (itr[1] == '/') {
+        toff = 1;
+        return SimpleXMLType::Close;
+    } else if (itr[1] == '?') {
+        toff = 1;
+        return SimpleXMLType::Processing;
+    } else if (itr[1] == '!') {
+        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])))) {
+            toff = sizeof("!DOCTYPE") - 1;
+            return SimpleXMLType::Doctype;
+        } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
+            toff = sizeof("![CDATA[") - 1;
+            return SimpleXMLType::CData;
+        } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
+            toff = sizeof("!--") - 1;
+            return SimpleXMLType::Comment;
+        } else if (itr + sizeof("<!>") - 1 < itrEnd) {
+            toff = sizeof("!") - 1;
+            return SimpleXMLType::DoctypeChild;
+        }
+        return SimpleXMLType::Open;
+    }
+    return SimpleXMLType::Open;
+}
+
+
 /************************************************************************/
 /* External Class Implementation                                        */
 /************************************************************************/
 
+const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
+{
+#ifdef THORVG_LOG_ENABLED
+    static const char* TYPE_NAMES[] = {
+        "Svg",
+        "G",
+        "Defs",
+        "Animation",
+        "Arc",
+        "Circle",
+        "Ellipse",
+        "Image",
+        "Line",
+        "Path",
+        "Polygon",
+        "Polyline",
+        "Rect",
+        "Text",
+        "TextArea",
+        "Tspan",
+        "Use",
+        "Video",
+        "ClipPath",
+        "Mask",
+        "Unknown",
+    };
+    return TYPE_NAMES[(int) type];
+#endif
+    return nullptr;
+}
+
+
+bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
+{
+#ifdef THORVG_LOG_ENABLED
+    const auto elementsNum = 1;
+    const char* const elements[] = { "title" };
+
+    for (unsigned int i = 0; i < elementsNum; ++i) {
+        if (!strncmp(tagName, elements[i], strlen(tagName))) {
+            return true;
+        }
+    }
+    return false;
+#else
+    return true;
+#endif
+}
+
 
 bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
 {
@@ -311,13 +347,11 @@ bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttr
         }
         tval[i] = '\0';
 
-#ifdef THORVG_LOG_ENABLED
         if (!func((void*)data, tmpBuf, tval)) {
-            if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) printf("SVG: Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]\n", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type).c_str(), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id->c_str() : "NO_ID", tmpBuf, tval ? tval : "NONE");
+            if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) {
+                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 : "NO_ID", tmpBuf, tval ? tval : "NONE");
+            }
         }
-#else
-        func((void*)data, tmpBuf, tval);
-#endif
     }
     return true;
 }
@@ -329,107 +363,63 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb
 
     if (!buf || !func) return false;
 
-#define CB(type, start, end)                                     \
-    do {                                                         \
-        size_t _sz = end - start;                                \
-        bool _ret;                                               \
-        _ret = func((void*)data, type, start, _sz);              \
-        if (!_ret)                                               \
-            return false;                                        \
-    } while (0)
-
     while (itr < itrEnd) {
         if (itr[0] == '<') {
-            if (itr + 1 >= itrEnd) {
-                CB(SimpleXMLType::Error, itr, itrEnd);
-                return false;
-            } else {
-                SimpleXMLType type;
-                size_t toff;
-                const char* p;
-
-                if (itr[1] == '/') {
-                    type = SimpleXMLType::Close;
-                    toff = 1;
-                } else if (itr[1] == '?') {
-                    type = SimpleXMLType::Processing;
-                    toff = 1;
-                } else if (itr[1] == '!') {
-                    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])))) {
-                        type = SimpleXMLType::Doctype;
-                        toff = sizeof("!DOCTYPE") - 1;
-                    } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
-                        type = SimpleXMLType::Comment;
-                        toff = sizeof("!--") - 1;
-                    } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
-                        type = SimpleXMLType::CData;
-                        toff = sizeof("![CDATA[") - 1;
-                    } else if (itr + sizeof("<!>") - 1 < itrEnd) {
-                        type = SimpleXMLType::DoctypeChild;
-                        toff = sizeof("!") - 1;
-                    } else {
-                        type = SimpleXMLType::Open;
-                        toff = 0;
+            //Invalid case
+            if (itr + 1 >= itrEnd) return false;
+
+            size_t toff = 0;
+            SimpleXMLType type = _getXMLType(itr, itrEnd, toff);
+
+            const char* p;
+            if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
+            else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
+            else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
+            else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
+
+            if (p) {
+                //Invalid case: '<' nested
+                if (*p == '<') return false;
+                const char *start, *end;
+
+                start = itr + 1 + toff;
+                end = p;
+
+                switch (type) {
+                    case SimpleXMLType::Open: {
+                        if (p[-1] == '/') {
+                            type = SimpleXMLType::OpenEmpty;
+                            end--;
+                        }
+                        break;
+                    }
+                    case SimpleXMLType::CData: {
+                        if (!memcmp(p - 2, "]]", 2)) end -= 2;
+                        break;
+                    }
+                    case SimpleXMLType::Processing: {
+                        if (p[-1] == '?') end--;
+                        break;
+                    }
+                    case SimpleXMLType::Comment: {
+                        if (!memcmp(p - 2, "--", 2)) end -= 2;
+                        break;
+                    }
+                    default: {
+                        break;
                     }
-                } else {
-                    type = SimpleXMLType::Open;
-                    toff = 0;
                 }
 
-                if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
-                else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
-                else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
-                else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
-
-                if ((p) && (*p == '<')) {
-                    type = SimpleXMLType::Error;
-                    toff = 0;
+                if (strip && (type != SimpleXMLType::CData)) {
+                    start = _skipWhiteSpacesAndXmlEntities(start, end);
+                    end = _unskipWhiteSpacesAndXmlEntities(end, start);
                 }
 
-                if (p) {
-                    const char *start, *end;
-
-                    start = itr + 1 + toff;
-                    end = p;
-
-                    switch (type) {
-                        case SimpleXMLType::Open: {
-                            if (p[-1] == '/') {
-                                type = SimpleXMLType::OpenEmpty;
-                                end--;
-                            }
-                            break;
-                        }
-                        case SimpleXMLType::CData: {
-                            if (!memcmp(p - 2, "]]", 2)) end -= 2;
-                            break;
-                        }
-                        case SimpleXMLType::Processing: {
-                            if (p[-1] == '?') end--;
-                            break;
-                        }
-                        case SimpleXMLType::Comment: {
-                            if (!memcmp(p - 2, "--", 2)) end -= 2;
-                            break;
-                        }
-                        default: {
-                            break;
-                        }
-                    }
-
-                    if ((strip) && (type != SimpleXMLType::Error) && (type != SimpleXMLType::CData)) {
-                        start = _skipWhiteSpacesAndXmlEntities(start, end);
-                        end = _unskipWhiteSpacesAndXmlEntities(end, start);
-                    }
-
-                    CB(type, start, end);
+                if (!func((void*)data, type, start, (unsigned int)(end - start))) return false;
 
-                    if (type != SimpleXMLType::Error) itr = p + 1;
-                    else itr = p;
-                } else {
-                    CB(SimpleXMLType::Error, itr, itrEnd);
-                    return false;
-                }
+                itr = p + 1;
+            } else {
+                return false;
             }
         } else {
             const char *p, *end;
@@ -438,7 +428,7 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb
                 p = itr;
                 p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
                 if (p) {
-                    CB(SimpleXMLType::Ignored, itr, p);
+                    if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
                     itr = p;
                 }
             }
@@ -449,21 +439,18 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb
             end = p;
             if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
 
-            if (itr != end) CB(SimpleXMLType::Data, itr, end);
+            if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (unsigned int)(end - itr))) return false;
 
-            if ((strip) && (end < p)) CB(SimpleXMLType::Ignored, end, p);
+            if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (unsigned int)(p - end))) return false;
 
             itr = p;
         }
     }
-
-#undef CB
-
     return true;
 }
 
 
-bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
+bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data)
 {
     const char* end;
     char* key;
@@ -472,7 +459,7 @@ bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, cons
 
     if (!buf) return false;
 
-    end = buf + strlen(buf);
+    end = buf + buflen;
     key = (char*)alloca(end - buf + 1);
     val = (char*)alloca(end - buf + 1);
 
@@ -481,6 +468,12 @@ bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, cons
     do {
         char* sep = (char*)strchr(buf, ':');
         next = (char*)strchr(buf, ';');
+        if (sep >= end)
+        {
+            next = nullptr;
+            sep = nullptr;
+        }
+        if (next >= end) next = nullptr;
 
         key[0] = '\0';
         val[0] = '\0';
@@ -503,7 +496,16 @@ bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, cons
         }
 
         if (key[0]) {
-            if (!func((void*)data, key, val)) return false;
+            key = const_cast<char*>(_simpleXmlSkipWhiteSpace(key, key + strlen(key)));
+            key[_simpleXmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0';
+            val = const_cast<char*>(_simpleXmlSkipWhiteSpace(val, val + strlen(val)));
+            val[_simpleXmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0';
+
+            if (!func((void*)data, key, val)) {
+                if (!_isIgnoreUnsupportedLogAttributes(key, val)) {
+                    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 : "NO_ID", key, val ? val : "NONE");
+                }
+            }
         }
 
         buf = next + 1;
@@ -513,6 +515,47 @@ bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, cons
 }
 
 
+/*
+ * Supported formats:
+ * tag {}, .name {}, tag.name{}
+ */
+const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength)
+{
+    if (!buf) return nullptr;
+
+    *tag = *name = nullptr;
+    *attrsLength = 0;
+
+    auto itr = _simpleXmlSkipWhiteSpace(buf, buf + bufLength);
+    auto itrEnd = (const char*)memchr(buf, '{', bufLength);
+
+    if (!itrEnd || itr == itrEnd) return nullptr;
+
+    auto nextElement = (const char*)memchr(itrEnd, '}', bufLength - (itrEnd - buf));
+    if (!nextElement) return nullptr;
+
+    *attrs = itrEnd + 1;
+    *attrsLength = nextElement - *attrs;
+
+    const char *p;
+
+    itrEnd = _simpleXmlUnskipWhiteSpace(itrEnd, itr);
+    if (*(itrEnd - 1) == '.') return nullptr;
+
+    for (p = itr; p < itrEnd; p++) {
+        if (*p == '.') break;
+    }
+
+    if (p == itr) *tag = strdup("all");
+    else *tag = strndup(itr, p - itr);
+
+    if (p == itrEnd) *name = nullptr;
+    else *name = strndup(p + 1, itrEnd - p - 1);
+
+    return (nextElement ? nextElement + 1 : nullptr);
+}
+
+
 const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
 {
     const char *itr = buf, *itrEnd = buf + bufLength;