Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / dll / 3rdparty / libxslt / attrvt.c
diff --git a/dll/3rdparty/libxslt/attrvt.c b/dll/3rdparty/libxslt/attrvt.c
new file mode 100644 (file)
index 0000000..a23b62a
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * attrvt.c: Implementation of the XSL Transformation 1.0 engine
+ *           attribute value template handling part.
+ *
+ * References:
+ *   http://www.w3.org/TR/1999/REC-xslt-19991116
+ *
+ *   Michael Kay "XSLT Programmer's Reference" pp 637-643
+ *   Writing Multiple Output Files
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#include "precomp.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_AVT
+#endif
+
+#define MAX_AVT_SEG 10
+
+typedef struct _xsltAttrVT xsltAttrVT;
+typedef xsltAttrVT *xsltAttrVTPtr;
+struct _xsltAttrVT {
+    struct _xsltAttrVT *next; /* next xsltAttrVT */
+    int nb_seg;                /* Number of segments */
+    int max_seg;       /* max capacity before re-alloc needed */
+    int strstart;      /* is the start a string */
+    /*
+     * the namespaces in scope
+     */
+    xmlNsPtr *nsList;
+    int nsNr;
+    /*
+     * the content is an alternate of string and xmlXPathCompExprPtr
+     */
+    void *segments[MAX_AVT_SEG];
+};
+
+/**
+ * xsltNewAttrVT:
+ * @style:  a XSLT process context
+ *
+ * Build a new xsltAttrVT structure
+ *
+ * Returns the structure or NULL in case of error
+ */
+static xsltAttrVTPtr
+xsltNewAttrVT(xsltStylesheetPtr style) {
+    xsltAttrVTPtr cur;
+
+    cur = (xsltAttrVTPtr) xmlMalloc(sizeof(xsltAttrVT));
+    if (cur == NULL) {
+       xsltTransformError(NULL, style, NULL,
+               "xsltNewAttrVTPtr : malloc failed\n");
+       if (style != NULL) style->errors++;
+       return(NULL);
+    }
+    memset(cur, 0, sizeof(xsltAttrVT));
+
+    cur->nb_seg = 0;
+    cur->max_seg = MAX_AVT_SEG;
+    cur->strstart = 0;
+    cur->next = style->attVTs;
+    /*
+     * Note: this pointer may be changed by a re-alloc within xsltCompileAttr,
+     * so that code may change the stylesheet pointer also!
+     */
+    style->attVTs = (xsltAttrVTPtr) cur;
+
+    return(cur);
+}
+
+/**
+ * xsltFreeAttrVT:
+ * @avt: pointer to an xsltAttrVT structure
+ *
+ * Free up the memory associated to the attribute value template
+ */
+static void
+xsltFreeAttrVT(xsltAttrVTPtr avt) {
+    int i;
+
+    if (avt == NULL) return;
+
+    if (avt->strstart == 1) {
+       for (i = 0;i < avt->nb_seg; i += 2)
+           if (avt->segments[i] != NULL)
+               xmlFree((xmlChar *) avt->segments[i]);
+       for (i = 1;i < avt->nb_seg; i += 2)
+           xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
+    } else {
+       for (i = 0;i < avt->nb_seg; i += 2)
+           xmlXPathFreeCompExpr((xmlXPathCompExprPtr) avt->segments[i]);
+       for (i = 1;i < avt->nb_seg; i += 2)
+           if (avt->segments[i] != NULL)
+               xmlFree((xmlChar *) avt->segments[i]);
+    }
+    if (avt->nsList != NULL)
+        xmlFree(avt->nsList);
+    xmlFree(avt);
+}
+
+/**
+ * xsltFreeAVTList:
+ * @avt: pointer to an list of AVT structures
+ *
+ * Free up the memory associated to the attribute value templates
+ */
+void
+xsltFreeAVTList(void *avt) {
+    xsltAttrVTPtr cur = (xsltAttrVTPtr) avt, next;
+
+    while (cur != NULL) {
+        next = cur->next;
+       xsltFreeAttrVT(cur);
+       cur = next;
+    }
+}
+/**
+ * xsltSetAttrVTsegment:
+ * @ avt: pointer to an xsltAttrVT structure
+ * @ val: the value to be set to the next available segment
+ *
+ * Within xsltCompileAttr there are several places where a value
+ * needs to be added to the 'segments' array within the xsltAttrVT
+ * structure, and at each place the allocated size may have to be
+ * re-allocated.  This routine takes care of that situation.
+ *
+ * Returns the avt pointer, which may have been changed by a re-alloc
+ */
+static xsltAttrVTPtr
+xsltSetAttrVTsegment(xsltAttrVTPtr avt, void *val) {
+    if (avt->nb_seg >= avt->max_seg) {
+       avt = (xsltAttrVTPtr) xmlRealloc(avt, sizeof(xsltAttrVT) +
+                       avt->max_seg * sizeof(void *));
+       if (avt == NULL) {
+           return NULL;
+       }
+       memset(&avt->segments[avt->nb_seg], 0, MAX_AVT_SEG*sizeof(void *));
+       avt->max_seg += MAX_AVT_SEG;
+    }
+    avt->segments[avt->nb_seg++] = val;
+    return avt;
+}
+
+/**
+ * xsltCompileAttr:
+ * @style:  a XSLT process context
+ * @attr: the attribute coming from the stylesheet.
+ *
+ * Precompile an attribute in a stylesheet, basically it checks if it is
+ * an attrubute value template, and if yes establish some structures needed
+ * to process it at transformation time.
+ */
+void
+xsltCompileAttr(xsltStylesheetPtr style, xmlAttrPtr attr) {
+    const xmlChar *str;
+    const xmlChar *cur;
+    xmlChar *ret = NULL;
+    xmlChar *expr = NULL;
+    xsltAttrVTPtr avt;
+    int i = 0, lastavt = 0;
+
+    if ((style == NULL) || (attr == NULL) || (attr->children == NULL))
+        return;
+    if ((attr->children->type != XML_TEXT_NODE) ||
+        (attr->children->next != NULL)) {
+        xsltTransformError(NULL, style, attr->parent,
+           "Attribute '%s': The content is expected to be a single text "
+           "node when compiling an AVT.\n", attr->name);
+       style->errors++;
+       return;
+    }
+    str = attr->children->content;
+    if ((xmlStrchr(str, '{') == NULL) &&
+        (xmlStrchr(str, '}') == NULL)) return;
+
+#ifdef WITH_XSLT_DEBUG_AVT
+    xsltGenericDebug(xsltGenericDebugContext,
+                   "Found AVT %s: %s\n", attr->name, str);
+#endif
+    if (attr->psvi != NULL) {
+#ifdef WITH_XSLT_DEBUG_AVT
+       xsltGenericDebug(xsltGenericDebugContext,
+                       "AVT %s: already compiled\n", attr->name);
+#endif
+        return;
+    }
+    /*
+    * Create a new AVT object.
+    */
+    avt = xsltNewAttrVT(style);
+    if (avt == NULL)
+       return;
+    attr->psvi = avt;
+
+    avt->nsList = xmlGetNsList(attr->doc, attr->parent);
+    if (avt->nsList != NULL) {
+       while (avt->nsList[i] != NULL)
+           i++;
+    }
+    avt->nsNr = i;
+
+    cur = str;
+    while (*cur != 0) {
+       if (*cur == '{') {
+           if (*(cur+1) == '{') {      /* escaped '{' */
+               cur++;
+               ret = xmlStrncat(ret, str, cur - str);
+               cur++;
+               str = cur;
+               continue;
+           }
+           if (*(cur+1) == '}') {      /* skip empty AVT */
+               ret = xmlStrncat(ret, str, cur - str);
+               cur += 2;
+               str = cur;
+               continue;
+           }
+           if ((ret != NULL) || (cur - str > 0)) {
+               ret = xmlStrncat(ret, str, cur - str);
+               str = cur;
+               if (avt->nb_seg == 0)
+                   avt->strstart = 1;
+               if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
+                   goto error;
+               ret = NULL;
+               lastavt = 0;
+           }
+
+           cur++;
+           while ((*cur != 0) && (*cur != '}')) {
+               /* Need to check for literal (bug539741) */
+               if ((*cur == '\'') || (*cur == '"')) {
+                   char delim = *(cur++);
+                   while ((*cur != 0) && (*cur != delim))
+                       cur++;
+                   if (*cur != 0)
+                       cur++;  /* skip the ending delimiter */
+               } else
+                   cur++;
+           }
+           if (*cur == 0) {
+               xsltTransformError(NULL, style, attr->parent,
+                    "Attribute '%s': The AVT has an unmatched '{'.\n",
+                    attr->name);
+               style->errors++;
+               goto error;
+           }
+           str++;
+           expr = xmlStrndup(str, cur - str);
+           if (expr == NULL) {
+               /*
+               * TODO: What needs to be done here?
+               */
+               XSLT_TODO
+               goto error;
+           } else {
+               xmlXPathCompExprPtr comp;
+
+               comp = xsltXPathCompile(style, expr);
+               if (comp == NULL) {
+                   xsltTransformError(NULL, style, attr->parent,
+                        "Attribute '%s': Failed to compile the expression "
+                        "'%s' in the AVT.\n", attr->name, expr);
+                   style->errors++;
+                   goto error;
+               }
+               if (avt->nb_seg == 0)
+                   avt->strstart = 0;
+               if (lastavt == 1) {
+                   if ((avt = xsltSetAttrVTsegment(avt, NULL)) == NULL)
+                       goto error;
+               }
+               if ((avt = xsltSetAttrVTsegment(avt, (void *) comp)) == NULL)
+                   goto error;
+               lastavt = 1;
+               xmlFree(expr);
+               expr = NULL;
+           }
+           cur++;
+           str = cur;
+       } else if (*cur == '}') {
+           cur++;
+           if (*cur == '}') {  /* escaped '}' */
+               ret = xmlStrncat(ret, str, cur - str);
+               cur++;
+               str = cur;
+               continue;
+           } else {
+               xsltTransformError(NULL, style, attr->parent,
+                    "Attribute '%s': The AVT has an unmatched '}'.\n",
+                    attr->name);
+               goto error;
+           }
+       } else
+           cur++;
+    }
+    if ((ret != NULL) || (cur - str > 0)) {
+       ret = xmlStrncat(ret, str, cur - str);
+       str = cur;
+       if (avt->nb_seg == 0)
+           avt->strstart = 1;
+       if ((avt = xsltSetAttrVTsegment(avt, (void *) ret)) == NULL)
+           goto error;
+       ret = NULL;
+    }
+
+error:
+    if (avt == NULL) {
+        xsltTransformError(NULL, style, attr->parent,
+               "xsltCompileAttr: malloc problem\n");
+    } else {
+        if (attr->psvi != avt) {  /* may have changed from realloc */
+            attr->psvi = avt;
+           /*
+            * This is a "hack", but I can't see any clean method of
+            * doing it.  If a re-alloc has taken place, then the pointer
+            * for this AVT may have changed.  style->attVTs was set by
+            * xsltNewAttrVT, so it needs to be re-set to the new value!
+            */
+           style->attVTs = avt;
+       }
+    }
+    if (ret != NULL)
+       xmlFree(ret);
+    if (expr != NULL)
+       xmlFree(expr);
+}
+
+
+/**
+ * xsltEvalAVT:
+ * @ctxt: the XSLT transformation context
+ * @avt: the prevompiled attribute value template info
+ * @node: the node hosting the attribute
+ *
+ * Process the given AVT, and return the new string value.
+ *
+ * Returns the computed string value or NULL, must be deallocated by the
+ *         caller.
+ */
+xmlChar *
+xsltEvalAVT(xsltTransformContextPtr ctxt, void *avt, xmlNodePtr node) {
+    xmlChar *ret = NULL, *tmp;
+    xmlXPathCompExprPtr comp;
+    xsltAttrVTPtr cur = (xsltAttrVTPtr) avt;
+    int i;
+    int str;
+
+    if ((ctxt == NULL) || (avt == NULL) || (node == NULL))
+        return(NULL);
+    str = cur->strstart;
+    for (i = 0;i < cur->nb_seg;i++) {
+        if (str) {
+           ret = xmlStrcat(ret, (const xmlChar *) cur->segments[i]);
+       } else {
+           comp = (xmlXPathCompExprPtr) cur->segments[i];
+           tmp = xsltEvalXPathStringNs(ctxt, comp, cur->nsNr, cur->nsList);
+           if (tmp != NULL) {
+               if (ret != NULL) {
+                   ret = xmlStrcat(ret, tmp);
+                   xmlFree(tmp);
+               } else {
+                   ret = tmp;
+               }
+           }
+       }
+       str = !str;
+    }
+    return(ret);
+}