added DTD like checking when compiling stylesheets, closes bug #160402 and
authorDaniel Veillard <veillard@src.gnome.org>
Tue, 29 Mar 2005 13:16:06 +0000 (13:16 +0000)
committerDaniel Veillard <veillard@src.gnome.org>
Tue, 29 Mar 2005 13:16:06 +0000 (13:16 +0000)
* libxslt/preproc.c libxslt/xslt.c libxslt/xsltInternals.h:
  added DTD like checking when compiling stylesheets, closes
  bug #160402 and a long term TODO
* tests/general/bug-89.xsl: thos spotted a misconstruct of one
  of the test cases where <xsl:when> was not within <xsl:choose>
Daniel

ChangeLog
doc/xsltproc.1
libxslt/preproc.c
libxslt/xslt.c
libxslt/xsltInternals.h
libxslt/xsltwin32config.h
tests/general/bug-89.xsl

index 51dd45a..465a548 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,14 @@
+Tue Mar 29 15:13:59 CEST 2005 Daniel Veillard <daniel@veillard.com>
+
+       * libxslt/preproc.c libxslt/xslt.c libxslt/xsltInternals.h: 
+         added DTD like checking when compiling stylesheets, closes
+         bug #160402 and a long term TODO
+       * tests/general/bug-89.xsl: thos spotted a misconstruct of one
+         of the test cases where <xsl:when> was not within <xsl:choose>
+
 Mon Mar 21 20:56:43 MST 2005 John Fleck (jfleck@inkstain.net)
 
-       * doc/xsltproc.xml, xsltproc.html, xsltproc.1
+       * doc/xsltproc.xml, xsltproc.html, xsltproc.1:
        update documentation to reflect addition of --nodtdattr
        command line flag. Bug #171098
 
index 8aaabbd..460bcc1 100644 (file)
@@ -54,7 +54,7 @@ Output each step taken by xsltproc in processing the stylesheet and the document
 
 .TP
 \fB\-o\fR or \fB\-\-output\fR \fIfile\fR
-Direct output to the file named \fIfile\fR\&. For multiple outputs, also known as "chunking", \fB\-o\fR \fBdirectory/\fR directs the output files to a specified directory\&. The directory must already exist\&.
+Direct output to the file named \fIfile\fR\&. For multiple outputs, also known as "chunking", \fB\-o\fR  \fBdirectory/\fR directs the output files to a specified directory\&. The directory must already exist\&.
 
 .TP
 \fB\-\-timing\fR
@@ -110,7 +110,7 @@ Use the SGML catalog specified in \fBSGML_CATALOG_FILES\fR to resolve the locati
 
 .TP
 \fB\-\-xinclude\fR
-Process the input document using the Xinclude specification\&. More details on this can be found in the Xinclude specification: http://www\&.w3\&.org/TR/xinclude/: \fIhttp://www.w3.org/TR/xinclude/\fR
+Process the input document using the Xinclude specification\&. More details on this can be found in the Xinclude specification: http://www\&.w3\&.org/TR/xinclude/: \fIhttp://www.w3.org/TR/xinclude/\fR 
 
 .TP
 \fB\-\-profile\fR or \fB\-\-norman\fR
index a55874d..e4e69dc 100644 (file)
@@ -48,6 +48,158 @@ const xmlChar *xsltExtMarker = (const xmlChar *) "Extension Element";
 
 /************************************************************************
  *                                                                     *
+ *                     Grammar checks                                  *
+ *                                                                     *
+ ************************************************************************/
+
+/**
+ * xsltCheckTopLevelElement:
+ * @style: the XSLT stylesheet
+ * @inst: the XSLT instruction
+ * @err: raise an error or not
+ *
+ * Check that the instruction is instanciated as a top level element.
+ *
+ * Returns -1 in case of error, 0 if failed and 1 in case of success
+ */
+static int
+xsltCheckTopLevelElement(xsltStylesheetPtr style, xmlNodePtr inst, int err) {
+    xmlNodePtr parent;
+    if ((style == NULL) || (inst == NULL) || (inst->ns == NULL))
+        return(-1);
+    
+    parent = inst->parent;
+    if (parent == NULL) {
+        if (err) {
+           xsltTransformError(NULL, style, inst,
+                   "internal problem: element has no parent\n");
+           style->errors++;
+       }
+       return(0);
+    }
+    if ((parent->ns == NULL) ||
+        ((parent->ns != inst->ns) &&
+        (!xmlStrEqual(parent->ns->href, inst->ns->href))) ||
+       ((!xmlStrEqual(parent->name, BAD_CAST "stylesheet")) &&
+        (!xmlStrEqual(parent->name, BAD_CAST "transform")))) {
+       if (err) {
+           xsltTransformError(NULL, style, inst,
+                   "element %s only allowed as child of stylesheet\n",
+                              inst->name);
+           style->errors++;
+       }
+       return(0);
+    }
+    return(1);
+}
+
+/**
+ * xsltCheckInstructionElement:
+ * @style: the XSLT stylesheet
+ * @inst: the XSLT instruction
+ *
+ * Check that the instruction is instanciated as an instruction element.
+ */
+static void
+xsltCheckInstructionElement(xsltStylesheetPtr style, xmlNodePtr inst) {
+    xmlNodePtr parent;
+    int has_ext;
+
+    if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) ||
+        (style->literal_result))
+        return;
+
+    has_ext = (style->extInfos != NULL);
+    
+    parent = inst->parent;
+    if (parent == NULL) {
+       xsltTransformError(NULL, style, inst,
+               "internal problem: element has no parent\n");
+       style->errors++;
+       return;
+    }
+    while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) {
+        if (((parent->ns == inst->ns) ||
+            ((parent->ns != NULL) &&
+             (xmlStrEqual(parent->ns->href, inst->ns->href)))) &&
+           ((xmlStrEqual(parent->name, BAD_CAST "template")) ||
+            (xmlStrEqual(parent->name, BAD_CAST "param")) ||
+            (xmlStrEqual(parent->name, BAD_CAST "attribute")) ||
+            (xmlStrEqual(parent->name, BAD_CAST "variable")))) {
+           return;
+       }
+
+       /*
+        * if we are within an extension element all bets are off
+        * about the semantic there e.g. xsl:param within func:function
+        */
+       if ((has_ext) && (parent->ns != NULL) &&
+           (xmlHashLookup(style->extInfos, parent->ns->href) != NULL))
+           return;
+       
+        parent = parent->parent;
+    }
+    xsltTransformError(NULL, style, inst,
+           "element %s only allowed within a template, variable or param\n",
+                          inst->name);
+    style->errors++;
+}
+
+/**
+ * xsltCheckParentElement:
+ * @style: the XSLT stylesheet
+ * @inst: the XSLT instruction
+ * @allow1: allowed parent1
+ * @allow2: allowed parent2
+ *
+ * Check that the instruction is instanciated as the childre of one of the
+ * possible parents.
+ */
+static void
+xsltCheckParentElement(xsltStylesheetPtr style, xmlNodePtr inst,
+                       const xmlChar *allow1, const xmlChar *allow2) {
+    xmlNodePtr parent;
+
+    if ((style == NULL) || (inst == NULL) || (inst->ns == NULL) ||
+        (style->literal_result))
+        return;
+
+    parent = inst->parent;
+    if (parent == NULL) {
+       xsltTransformError(NULL, style, inst,
+               "internal problem: element has no parent\n");
+       style->errors++;
+       return;
+    }
+    if (((parent->ns == inst->ns) ||
+        ((parent->ns != NULL) &&
+         (xmlStrEqual(parent->ns->href, inst->ns->href)))) &&
+       ((xmlStrEqual(parent->name, allow1)) ||
+        (xmlStrEqual(parent->name, allow2)))) {
+       return;
+    }
+
+    if (style->extInfos != NULL) {
+       while ((parent != NULL) && (parent->type != XML_DOCUMENT_NODE)) {
+           /*
+            * if we are within an extension element all bets are off
+            * about the semantic there e.g. xsl:param within func:function
+            */
+           if ((parent->ns != NULL) &&
+               (xmlHashLookup(style->extInfos, parent->ns->href) != NULL))
+               return;
+           
+           parent = parent->parent;
+       }
+    }
+    xsltTransformError(NULL, style, inst,
+                      "element %s is not allowed within that context\n",
+                      inst->name);
+    style->errors++;
+}
+
+/************************************************************************
+ *                                                                     *
  *                     handling of precomputed data                    *
  *                                                                     *
  ************************************************************************/
@@ -1192,91 +1344,129 @@ xsltStylePreCompute(xsltStylesheetPtr style, xmlNodePtr inst) {
        xsltStylePreCompPtr cur;
 
        if (IS_XSLT_NAME(inst, "apply-templates")) {
+           xsltCheckInstructionElement(style, inst);
            xsltApplyTemplatesComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "with-param")) {
+           xsltCheckParentElement(style, inst, BAD_CAST "apply-templates",
+                                  BAD_CAST "call-template");
            xsltWithParamComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "value-of")) {
+           xsltCheckInstructionElement(style, inst);
            xsltValueOfComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "copy")) {
+           xsltCheckInstructionElement(style, inst);
            xsltCopyComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "copy-of")) {
+           xsltCheckInstructionElement(style, inst);
            xsltCopyOfComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "if")) {
+           xsltCheckInstructionElement(style, inst);
            xsltIfComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "when")) {
+           xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL);
            xsltWhenComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "choose")) {
+           xsltCheckInstructionElement(style, inst);
            xsltChooseComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "for-each")) {
+           xsltCheckInstructionElement(style, inst);
            xsltForEachComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "apply-imports")) {
+           xsltCheckInstructionElement(style, inst);
            xsltApplyImportsComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "attribute")) {
+           xmlNodePtr parent = inst->parent;
+
+           if ((parent == NULL) || (parent->ns == NULL) ||
+               ((parent->ns != inst->ns) &&
+                (!xmlStrEqual(parent->ns->href, inst->ns->href))) ||
+               (!xmlStrEqual(parent->name, BAD_CAST "attribute-set"))) {
+               xsltCheckInstructionElement(style, inst);
+           }
            xsltAttributeComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "element")) {
+           xsltCheckInstructionElement(style, inst);
            xsltElementComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "text")) {
+           xsltCheckInstructionElement(style, inst);
            xsltTextComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "sort")) {
+           xsltCheckParentElement(style, inst, BAD_CAST "apply-templates",
+                                  BAD_CAST "for-each");
            xsltSortComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "comment")) {
+           xsltCheckInstructionElement(style, inst);
            xsltCommentComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "number")) {
+           xsltCheckInstructionElement(style, inst);
            xsltNumberComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "processing-instruction")) {
+           xsltCheckInstructionElement(style, inst);
            xsltProcessingInstructionComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "call-template")) {
+           xsltCheckInstructionElement(style, inst);
            xsltCallTemplateComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "param")) {
+           if (xsltCheckTopLevelElement(style, inst, 0) == 0)
+               xsltCheckInstructionElement(style, inst);
            xsltParamComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "variable")) {
+           if (xsltCheckTopLevelElement(style, inst, 0) == 0)
+               xsltCheckInstructionElement(style, inst);
            xsltVariableComp(style, inst);
        } else if (IS_XSLT_NAME(inst, "otherwise")) {
-           /* no computation needed */
+           xsltCheckParentElement(style, inst, BAD_CAST "choose", NULL);
+           xsltCheckInstructionElement(style, inst);
            return;
        } else if (IS_XSLT_NAME(inst, "template")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "output")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "preserve-space")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "strip-space")) {
-           /* no computation needed */
-           return;
-       } else if (IS_XSLT_NAME(inst, "stylesheet")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
-       } else if (IS_XSLT_NAME(inst, "transform")) {
-           /* no computation needed */
+       } else if ((IS_XSLT_NAME(inst, "stylesheet")) ||
+                  (IS_XSLT_NAME(inst, "transform"))) {
+           xmlNodePtr parent = inst->parent;
+
+           if ((parent == NULL) || (parent->type != XML_DOCUMENT_NODE)) {
+               xsltTransformError(NULL, style, inst,
+                   "element %s only allowed only as root element\n",
+                                  inst->name);
+               style->errors++;
+           }
            return;
        } else if (IS_XSLT_NAME(inst, "key")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "message")) {
-           /* no computation needed */
+           xsltCheckInstructionElement(style, inst);
            return;
        } else if (IS_XSLT_NAME(inst, "attribute-set")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "namespace-alias")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "include")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "import")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "decimal-format")) {
-           /* no computation needed */
+           xsltCheckTopLevelElement(style, inst, 1);
            return;
        } else if (IS_XSLT_NAME(inst, "fallback")) {
-           /* no computation needed */
+           xsltCheckInstructionElement(style, inst);
            return;
        } else if (IS_XSLT_NAME(inst, "document")) {
+           xsltCheckInstructionElement(style, inst);
            inst->psvi = (void *) xsltDocumentComp(style, inst,
                                (xsltTransformFunction) xsltDocumentElem);
        } else {
index cae7a18..9be9533 100644 (file)
@@ -375,6 +375,7 @@ xsltNewStylesheet(void) {
     cur->extInfos = NULL;
     cur->extrasNr = 0;
     cur->internalized = 1;
+    cur->literal_result = 0;
     cur->dict = xmlDictCreate();
 #ifdef WITH_XSLT_DEBUG
     xsltGenericDebug(xsltGenericDebugContext,
@@ -1901,8 +1902,6 @@ xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) {
        xmlFree(prop);
     }
 
-    xsltParseStylesheetExtPrefix(style, top);
-
     cur = top->children;
 
     /*
@@ -2048,9 +2047,6 @@ xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) {
        return(NULL);
     }
     xsltParseStylesheetExcludePrefix(ret, cur);
-    if (!ret->nopreproc)
-       xsltPrecomputeStylesheet(ret, cur);
-
     if ((IS_XSLT_ELEM(cur)) && 
        ((IS_XSLT_NAME(cur, "stylesheet")) ||
         (IS_XSLT_NAME(cur, "transform")))) {
@@ -2058,6 +2054,16 @@ xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) {
        xsltGenericDebug(xsltGenericDebugContext,
                "xsltParseStylesheetProcess : found stylesheet\n");
 #endif
+       ret->literal_result = 0;
+
+       xsltParseStylesheetExtPrefix(ret, cur);
+    } else {
+       ret->literal_result = 1;
+    }
+    if (!ret->nopreproc)
+       xsltPrecomputeStylesheet(ret, cur);
+
+    if (ret->literal_result == 0) {
 
        xsltParseStylesheetTop(ret, cur);
     } else {
@@ -2105,6 +2111,7 @@ xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) {
        template->elem = (xmlNodePtr) doc;
        template->content = doc->children;
        xsltAddTemplate(ret, template, NULL, NULL);
+       ret->literal_result = 1;
     }
 
     return(ret);
index 807e2a6..193316f 100644 (file)
@@ -447,6 +447,10 @@ struct _xsltStylesheet {
      * all document text strings were internalized
      */
     int internalized;
+    /*
+     * Literal Result Element as Stylesheet c.f. section 2.3
+     */
+    int literal_result;
 };
 
 /*
index 5369727..f4864cd 100644 (file)
@@ -44,7 +44,7 @@ extern "C" {
  *
  * extra version information, used to show a CVS compilation
  */
-#define LIBXSLT_VERSION_EXTRA "-CVS998"
+#define LIBXSLT_VERSION_EXTRA "-CVS999"
 
 /**
  * WITH_XSLT_DEBUG:
index 502fb7a..4bf4ca1 100644 (file)
@@ -44,9 +44,12 @@ la valeur nulle -->
 </xsl:call-template>
 </xsl:when>
 <xsl:when test="$action='repl'">
+<xsl:choose>
 <xsl:when test="$action='del'">
 <xsl:call-template name="del"/>
-</xsl:when> <xsl:call-template name="add">
+</xsl:when>
+</xsl:choose>
+<xsl:call-template name="add">
 <xsl:with-param name="type">toto</xsl:with-param>
 </xsl:call-template>
 </xsl:when> </xsl:choose>