More general work, added for-each:
authorDaniel Veillard <veillard@src.gnome.org>
Sat, 13 Jan 2001 22:29:51 +0000 (22:29 +0000)
committerDaniel Veillard <veillard@src.gnome.org>
Sat, 13 Jan 2001 22:29:51 +0000 (22:29 +0000)
- test/Makefile.am test/REC*/Makefile.am: added first test
- libxslt/pattern.c libxslt/transform.c libxslt/xslt.c:
  cleanup of nodes at reading of stylesheet, added support
  for xsl:for-each and fixed a few recursion bugs
Daniel

ChangeLog
Makefile.am
configure.in
libxslt/pattern.c
libxslt/transform.c
libxslt/xslt.c
tests/Makefile.am
tests/REC1/Makefile.am [new file with mode: 0644]
tests/REC1/result.xml
tests/REC2/Makefile.am [new file with mode: 0644]

index 22037e9..4678f82 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Sat Jan 13 23:26:21 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
+
+       * test/Makefile.am test/REC*/Makefile.am: added first test
+       * libxslt/pattern.c libxslt/transform.c libxslt/xslt.c:
+         cleanup of nodes at reading of stylesheet, added support
+         for xsl:for-each and fixed a few recursion bugs
+
 Fri Jan 12 22:33:07 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
 
        * pattern.c, xslt.c: removed debug
index a34c42d..09ee109 100644 (file)
@@ -24,6 +24,7 @@ xsltConf.sh: xsltConf.sh.in Makefile
               < $(srcdir)/xsltConf.sh.in > xsltConf.tmp \
        && mv xsltConf.tmp xsltConf.sh
 
-
+test:
+       @(cd tests ; make test)
 
 
index 7f6b287..d506a87 100644 (file)
@@ -120,5 +120,7 @@ Makefile
 libxslt/Makefile
 libxslt/xsltconfig.h
 tests/Makefile
+tests/REC1/Makefile
+tests/REC2/Makefile
 xslt-config
 ])
index 9dddab1..e1510d1 100644 (file)
@@ -571,8 +571,8 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
        NEXT;
        SKIP_BLANKS;
        PUSH(XSLT_OP_ROOT, NULL, NULL);
-       PUSH(XSLT_OP_PARENT, NULL, NULL);
        if ((CUR != 0) || (CUR == '|')) {
+           PUSH(XSLT_OP_PARENT, NULL, NULL);
            xsltCompileRelativePathPattern(ctxt, NULL);
        }
     } else {
index 7c2b228..8d2d337 100644 (file)
@@ -89,7 +89,6 @@ struct _xsltTransformContext {
     xmlNodePtr insert;                 /* the insertion node */
 
     xmlXPathContextPtr xpathCtxt;      /* the XPath context */
-    xmlXPathParserContextPtr xpathParserCtxt;/* the XPath parser context */
 };
 
 /************************************************************************
@@ -142,6 +141,8 @@ xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
  ************************************************************************/
 
 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
+void xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                xmlNodePtr inst);
 
 /**
  * xsltValueOf:
@@ -157,6 +158,7 @@ xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
     xmlChar *prop;
     int disableEscaping = 0;
     xmlXPathObjectPtr res, tmp;
+    xmlXPathParserContextPtr xpathParserCtxt;
     xmlNodePtr copy = NULL;
 
     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
@@ -181,7 +183,7 @@ xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
     if (prop == NULL) {
        xsltGenericError(xsltGenericErrorContext,
-            "xsltValueOf: select is not defined\n", prop);
+            "xsltValueOf: select is not defined\n");
        return;
     }
 #ifdef DEBUG_PROCESS
@@ -195,17 +197,17 @@ xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
        if (ctxt->xpathCtxt == NULL)
            goto error;
     }
-    ctxt->xpathParserCtxt =
+    xpathParserCtxt =
        xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
-    if (ctxt->xpathParserCtxt == NULL)
+    if (xpathParserCtxt == NULL)
        goto error;
     ctxt->xpathCtxt->node = node;
-    valuePush(ctxt->xpathParserCtxt, xmlXPathNewNodeSet(node));
-    xmlXPathEvalExpr(ctxt->xpathParserCtxt);
-    xmlXPathStringFunction(ctxt->xpathParserCtxt, 1);
-    res = valuePop(ctxt->xpathParserCtxt);
+    valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
+    xmlXPathEvalExpr(xpathParserCtxt);
+    xmlXPathStringFunction(xpathParserCtxt, 1);
+    res = valuePop(xpathParserCtxt);
     do {
-        tmp = valuePop(ctxt->xpathParserCtxt);
+        tmp = valuePop(xpathParserCtxt);
        if (tmp != NULL) {
            xmlXPathFreeObject(tmp);
        }
@@ -228,8 +230,10 @@ xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
             "xsltValueOf: result %s\n", res->stringval);
 #endif
 error:
-    if (ctxt->xpathParserCtxt != NULL)
-       xmlXPathFreeParserContext(ctxt->xpathParserCtxt);
+    if (xpathParserCtxt != NULL) {
+       xmlXPathFreeParserContext(xpathParserCtxt);
+        xpathParserCtxt = NULL;
+    }
     if (prop != NULL)
        xmlFree(prop);
     if (res != NULL)
@@ -253,6 +257,7 @@ xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
     xmlNodePtr copy;
 
     copy = xmlCopyNode(node, 0);
+    copy->doc = ctxt->output;
     if (copy != NULL) {
        xmlAddChild(insert, copy);
        /*
@@ -290,7 +295,7 @@ xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
        }
     } else {
        xsltGenericError(xsltGenericErrorContext,
-               "xsltProcessOneNode: copy %s failed\n", node->name);
+               "xsltCopyNode: copy %s failed\n", node->name);
     }
     return(copy);
 }
@@ -319,6 +324,7 @@ xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
 void
 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
     xmlNodePtr copy;
+    xmlNodePtr delete = NULL;
 
     switch (node->type) {
        case XML_DOCUMENT_NODE:
@@ -347,8 +353,10 @@ xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
                          xmlHashLookup(ctxt->style->stripSpaces,
                                        node->parent->name);
                    if ((val != NULL) &&
-                       (xmlStrEqual(val, (xmlChar *) "strip")))
+                       (xmlStrEqual(val, (xmlChar *) "strip"))) {
+                       delete = node;
                        break;
+                   }
                }
                /* no break on purpose */
            case XML_CDATA_SECTION_NODE:
@@ -361,9 +369,23 @@ xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
                }
                break;
            default:
-               TODO
+#ifdef DEBUG_PROCESS
+               xsltGenericError(xsltGenericErrorContext,
+                "xsltDefaultProcessOneNode: skipping node type %d\n",
+                                node->type);
+#endif
+               delete = node;
        }
        node = node->next;
+       if (delete != NULL) {
+#ifdef DEBUG_PROCESS
+           xsltGenericError(xsltGenericErrorContext,
+                "xsltDefaultProcessOneNode: removing ignorable blank node\n");
+#endif
+           xmlUnlinkNode(delete);
+           xmlFreeNode(delete);
+           delete = NULL;
+       }
     }
 }
 
@@ -396,38 +418,48 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
 }
 
 /**
- * xsltProcessOneNode:
+ * xsltApplyOneTemplate:
  * @ctxt:  a XSLT process context
  * @node:  the node in the source tree.
+ * @list:  the template replacement nodelist
  *
- * Process the source node.
+ * Process the apply-templates node on the source node
  */
 void
-xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
-    xsltTemplatePtr template;
-    xmlNodePtr cur, insert, copy;
+xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                    xmlNodePtr list) {
+    xmlNodePtr cur, insert, copy, delete = NULL;
     xmlNodePtr oldInsert;
 
     oldInsert = insert = ctxt->insert;
-    template = xsltGetTemplate(ctxt->style, node);
     /*
-     * If no template is found, apply the deafult rule.
+     * Insert all non-XSLT nodes found in the template
      */
-    if (template == NULL) {
+    cur = list;
+    while (cur != NULL) {
+       /*
+        * test, we must have a valid insertion point
+        */
+       if (insert == NULL) {
 #ifdef DEBUG_PROCESS
-       xsltGenericError(xsltGenericErrorContext,
-            "xsltProcessOneNode: no template found for %s\n", node->name);
+           xsltGenericError(xsltGenericErrorContext,
+                "xsltApplyOneTemplate: insert == NULL !\n");
 #endif
+           return;
+       }
 
-       xsltDefaultProcessOneNode(ctxt, node);
-       return;
-    }
-
-    /*
-     * Insert all non-XSLT nodes found in the template
-     */
-    cur = template->content;
-    while (cur != NULL) {
+       /*
+        * Cleanup of ignorable blank node detected
+        */
+       if (delete != NULL) {
+#ifdef DEBUG_PROCESS
+           xsltGenericError(xsltGenericErrorContext,
+                "xsltApplyOneTemplate: removing ignorable blank node\n");
+#endif
+           xmlUnlinkNode(delete);
+           xmlFreeNode(delete);
+           delete = NULL;
+       }
        if (IS_XSLT_ELEM(cur)) {
            if (IS_XSLT_NAME(cur, "apply-templates")) {
                ctxt->insert = insert;
@@ -437,13 +469,18 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
                ctxt->insert = insert;
                xsltValueOf(ctxt, node, cur);
                ctxt->insert = oldInsert;
+           } else if (IS_XSLT_NAME(cur, "for-each")) {
+               ctxt->insert = insert;
+               xsltForEach(ctxt, node, cur);
+               ctxt->insert = oldInsert;
            } else {
 #ifdef DEBUG_PROCESS
                xsltGenericError(xsltGenericErrorContext,
-                    "xsltProcessOneNode: found xslt:%s\n", cur->name);
+                    "xsltApplyOneTemplate: found xslt:%s\n", cur->name);
 #endif
                TODO
            }
+           goto skip_children;
        } else if (cur->type == XML_TEXT_NODE) {
            /*
             * This text comes from the stylesheet
@@ -453,20 +490,22 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
            if (!(IS_BLANK_NODE(cur))) {
 #ifdef DEBUG_PROCESS
                xsltGenericError(xsltGenericErrorContext,
-                    "xsltProcessOneNode: copy text %s\n", cur->content);
+                    "xsltApplyOneTemplate: copy text %s\n", cur->content);
 #endif
                copy = xmlCopyNode(cur, 0);
                if (copy != NULL) {
                    xmlAddChild(insert, copy);
                } else {
                    xsltGenericError(xsltGenericErrorContext,
-                           "xsltProcessOneNode: text copy failed\n");
+                           "xsltApplyOneTemplate: text copy failed\n");
                }
+           } else {
+               delete = cur;
            }
-       } else  {
+       } else if (cur->type == XML_ELEMENT_NODE) {
 #ifdef DEBUG_PROCESS
            xsltGenericError(xsltGenericErrorContext,
-                "xsltProcessOneNode: copy node %s\n", cur->name);
+                "xsltApplyOneTemplate: copy node %s\n", cur->name);
 #endif
            copy = xsltCopyNode(ctxt, cur, insert);
            /*
@@ -476,17 +515,19 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
            if (cur->properties != NULL)
                copy->properties = xmlCopyPropList(copy, cur->properties);
        }
+
        /*
-        * Skip to next node
+        * Skip to next node, in document order.
         */
-
        if (cur->children != NULL) {
            if (cur->children->type != XML_ENTITY_DECL) {
                cur = cur->children;
-               insert = copy;
+               if (copy != NULL)
+                   insert = copy;
                continue;
            }
        }
+skip_children:
        if (cur->next != NULL) {
            cur = cur->next;
            continue;
@@ -497,7 +538,7 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
            insert = insert->parent;
            if (cur == NULL)
                break;
-           if (cur == template->content) {
+           if (cur == list->parent) {
                cur = NULL;
                break;
            }
@@ -507,13 +548,126 @@ xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
            }
        } while (cur != NULL);
     }
-    /********
-    if (ctxt->style->indent) {
-       copy = xmlNewText("\n");
-       if (copy != NULL)
-           xmlAddChild(ctxt->insert, copy);
+}
+
+/**
+ * xsltForEach:
+ * @ctxt:  a XSLT process context
+ * @node:  the node in the source tree.
+ * @inst:  the xslt for-each node
+ *
+ * Process the xslt for-each node on the source node
+ */
+void
+xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                  xmlNodePtr inst) {
+    xmlChar *prop;
+    xmlXPathObjectPtr res, tmp;
+    xmlNodePtr replacement;
+    xmlNodeSetPtr list = NULL, oldlist;
+    xmlXPathParserContextPtr xpathParserCtxt;
+    int i;
+
+    if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
+       return;
+
+    prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
+    if (prop == NULL) {
+       xsltGenericError(xsltGenericErrorContext,
+            "xsltForEach: select is not defined\n");
+       return;
+    }
+#ifdef DEBUG_PROCESS
+    xsltGenericError(xsltGenericErrorContext,
+        "xsltForEach: select %s\n", prop);
+#endif
+
+    if (ctxt->xpathCtxt == NULL) {
+       xmlXPathInit();
+       ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
+       if (ctxt->xpathCtxt == NULL)
+           goto error;
+    }
+    xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
+    if (xpathParserCtxt == NULL)
+       goto error;
+    ctxt->xpathCtxt->node = node;
+    valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
+    xmlXPathEvalExpr(xpathParserCtxt);
+    res = valuePop(xpathParserCtxt);
+    do {
+        tmp = valuePop(xpathParserCtxt);
+       if (tmp != NULL) {
+           xmlXPathFreeObject(tmp);
+       }
+    } while (tmp != NULL);
+
+    if (res != NULL) {
+       if (res->type == XPATH_NODESET)
+           list = res->nodesetval;
+       else {
+#ifdef DEBUG_PROCESS
+           xsltGenericError(xsltGenericErrorContext,
+               "xsltForEach: select didn't evaluate to a node list\n");
+#endif
+           goto error;
+       }
+    }
+
+#ifdef DEBUG_PROCESS
+    xsltGenericError(xsltGenericErrorContext,
+       "xsltForEach: select evaluate to %d nodes\n", list->nodeNr);
+#endif
+    /* TODO: handle and skip the xsl:sort */
+    replacement = inst->children;
+
+    oldlist = ctxt->nodeList;
+    ctxt->nodeList = list;
+    for (i = 0;i < list->nodeNr;i++) {
+       ctxt->node = list->nodeTab[i];
+       xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement);
     }
-    ********/
+    ctxt->nodeList = oldlist;
+
+error:
+    if (xpathParserCtxt != NULL)
+       xmlXPathFreeParserContext(xpathParserCtxt);
+    if (prop != NULL)
+       xmlFree(prop);
+    if (res != NULL)
+       xmlXPathFreeObject(res);
+}
+
+/**
+ * xsltProcessOneNode:
+ * @ctxt:  a XSLT process context
+ * @node:  the node in the source tree.
+ *
+ * Process the source node.
+ */
+void
+xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
+    xsltTemplatePtr template;
+    template = xsltGetTemplate(ctxt->style, node);
+
+    /*
+     * If no template is found, apply the default rule.
+     */
+    if (template == NULL) {
+#ifdef DEBUG_PROCESS
+       if (node->type == XML_DOCUMENT_NODE)
+           xsltGenericError(xsltGenericErrorContext,
+            "xsltProcessOneNode: no template found for /\n");
+       else 
+           xsltGenericError(xsltGenericErrorContext,
+            "xsltProcessOneNode: no template found for %s\n", node->name);
+#endif
+
+       xsltDefaultProcessOneNode(ctxt, node);
+       return;
+    }
+
+    xsltApplyOneTemplate(ctxt, node, template->content);
 }
 
 /**
@@ -569,17 +723,10 @@ xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc) {
     /*
      * Start.
      */
-    root = xmlDocGetRootElement(doc);
-    if (root == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
-                        "xsltApplyStylesheet: document has no root\n");
-       goto error;
-    }
     ctxt->output = res;
     ctxt->insert = (xmlNodePtr) res;
-    ctxt->node = root;
-    ctxt->nodeList = xmlXPathNodeSetCreate(root);
-    xsltProcessOneNode(ctxt, root);
+    ctxt->node = (xmlNodePtr) doc;
+    xsltProcessOneNode(ctxt, ctxt->node);
 
 
     if ((ctxt->type = XSLT_OUTPUT_XML) &&
index 8635976..c6dee96 100644 (file)
@@ -477,7 +477,7 @@ xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
 void
 xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) {
     xsltTemplatePtr ret;
-    xmlNodePtr cur;
+    xmlNodePtr cur, delete;
     xmlChar *prop;
 
     if (template == NULL)
@@ -540,6 +540,65 @@ xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) {
     }
 
     /*
+     * Clean-up the template content from unwanted ignorable blank nodes
+     * This content comes from the stylesheet
+     * For stylesheets, the set of whitespace-preserving
+     * element names consists of just xsl:text.
+     */
+    cur = template->children;
+    delete = NULL;
+    while (cur != NULL) {
+       if (delete != NULL) {
+#ifdef DEBUG_PARSING
+           xsltGenericError(xsltGenericErrorContext,
+            "xsltParseStylesheetTemplate: removing ignorable blank node\n");
+#endif
+           xmlUnlinkNode(delete);
+           xmlFreeNode(delete);
+           delete = NULL;
+       }
+       if (IS_XSLT_ELEM(cur)) {
+           if (IS_XSLT_NAME(cur, "text"))
+               goto skip_children;
+       } else if (cur->type == XML_TEXT_NODE) {
+           if (IS_BLANK_NODE(cur)) {
+               delete = cur;
+           }
+       } else if (cur->type != XML_ELEMENT_NODE) {
+           delete = cur;
+       }
+
+       /*
+        * Skip to next node
+        */
+       if (cur->children != NULL) {
+           if (cur->children->type != XML_ENTITY_DECL) {
+               cur = cur->children;
+               continue;
+           }
+       }
+skip_children:
+       if (cur->next != NULL) {
+           cur = cur->next;
+           continue;
+       }
+       
+       do {
+           cur = cur->parent;
+           if (cur == NULL)
+               break;
+           if (cur == template) {
+               cur = NULL;
+               break;
+           }
+           if (cur->next != NULL) {
+               cur = cur->next;
+               break;
+           }
+       } while (cur != NULL);
+    }
+
+    /*
      * Find and handle the params
      */
     cur = template->children;
@@ -726,22 +785,24 @@ xsltParseStylesheetDoc(xmlDocPtr doc) {
        return(NULL);
     }
 
+    ret->doc = doc;
     if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "stylesheet"))) {
 #ifdef DEBUG_PARSING
        xsltGenericError(xsltGenericErrorContext,
                "xsltParseStylesheetDoc : found stylesheet\n");
 #endif
-    } else {
 
-       TODO /* lookup the stylesheet element down in the tree */
+       xsltParseStylesheetTop(ret, cur);
+    } else {
+       /*
+        * the document itself is the template.
+        */
+#ifdef DEBUG_PARSING
         xsltGenericError(xsltGenericErrorContext,
-               "xsltParseStylesheetDoc : root is not stylesheet\n");
-       xsltFreeStylesheet(ret);
-       return(NULL);
+               "xsltParseStylesheetDoc : document is stylesheet\n");
+#endif
+       TODO
     }
-    ret->doc = doc;
-
-    xsltParseStylesheetTop(ret, cur);
 
     return(ret);
 }
index 083886f..f682102 100644 (file)
@@ -17,3 +17,7 @@ LDADDS = $(top_builddir)/libxslt/libxslt.la $(LIBXML_LIBS)
 #testevents_LDFLAGS =
 #testevents_DEPENDENCIES = $(DEPS)
 #testevents_LDADD = $(LDADDS)
+
+test: $(top_builddir)/libxslt/xsltproc
+       @(cd REC1 ; make test)
+       @(cd REC2 ; make test)
diff --git a/tests/REC1/Makefile.am b/tests/REC1/Makefile.am
new file mode 100644 (file)
index 0000000..a01976a
--- /dev/null
@@ -0,0 +1,13 @@
+## Process this file with automake to produce Makefile.in
+
+$(top_builddir)/libxslt/xsltproc:
+       @(cd ../../libxslt ; make xsltproc)
+
+test: $(top_builddir)/libxslt/xsltproc
+       @(rm -f .memdump ; touch .memdump)
+       @($(top_builddir)/libxslt/xsltproc doc.xsl doc.xml > doc.res ; \
+       diff result.xml doc.res ; \
+       grep "MORY ALLO" .memdump  | grep -v "MEMORY ALLOCATED : 0";\
+       rm -f doc.res)
+
+
index 4504627..4e7836b 100644 (file)
@@ -1,18 +1,16 @@
 <?xml version="1.0" encoding="iso-8859-1"?>
 <html xmlns="http://www.w3.org/TR/xhtml1/strict">
-<head>
-<title>Document Title</title>
-</head>
-<body>
-<h1>Document Title</h1>
-<h2>Chapter Title</h2>
-<h3>Section Title</h3>
-<p>This is a test.</p>
-<p class="note">
-<b>NOTE: </b>This is a note.</p>
-<h3>Another Section Title</h3>
-<p>This is <em>another</em> test.</p>
-<p class="note">
-<b>NOTE: </b>This is another note.</p>
-</body>
+  <head>
+    <title>Document Title</title>
+  </head>
+  <body>
+    <h1>Document Title</h1>
+    <h2>Chapter Title</h2>
+    <h3>Section Title</h3>
+    <p>This is a test.</p>
+    <p class="note"><b>NOTE: </b>This is a note.</p>
+    <h3>Another Section Title</h3>
+    <p>This is <em>another</em> test.</p>
+    <p class="note"><b>NOTE: </b>This is another note.</p>
+  </body>
 </html>
diff --git a/tests/REC2/Makefile.am b/tests/REC2/Makefile.am
new file mode 100644 (file)
index 0000000..a01976a
--- /dev/null
@@ -0,0 +1,13 @@
+## Process this file with automake to produce Makefile.in
+
+$(top_builddir)/libxslt/xsltproc:
+       @(cd ../../libxslt ; make xsltproc)
+
+test: $(top_builddir)/libxslt/xsltproc
+       @(rm -f .memdump ; touch .memdump)
+       @($(top_builddir)/libxslt/xsltproc doc.xsl doc.xml > doc.res ; \
+       diff result.xml doc.res ; \
+       grep "MORY ALLO" .memdump  | grep -v "MEMORY ALLOCATED : 0";\
+       rm -f doc.res)
+
+