Some infrastructure work, and of course some debug:
[platform/upstream/libxslt.git] / libxslt / transform.c
1 /*
2  * transform.c: Implemetation of the XSL Transformation 1.0 engine
3  *            transform part, i.e. applying a Stylesheet to a document
4  *
5  * Reference:
6  *   http://www.w3.org/TR/1999/REC-xslt-19991116
7  *
8  * See Copyright for the status of this software.
9  *
10  * Daniel.Veillard@imag.fr
11  */
12
13 #include "xsltconfig.h"
14
15 #include <string.h>
16
17 #include <libxml/xmlmemory.h>
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20 #include <libxml/valid.h>
21 #include <libxml/hash.h>
22 #include <libxml/encoding.h>
23 #include <libxml/xmlerror.h>
24 #include <libxml/xpath.h>
25 #include <libxml/xpathInternals.h>
26 #include <libxml/HTMLtree.h>
27 #include "xslt.h"
28 #include "xsltInternals.h"
29 #include "xsltutils.h"
30 #include "pattern.h"
31 #include "transform.h"
32
33 #define DEBUG_PROCESS
34
35 /*
36  * Useful macros
37  */
38
39 #define IS_BLANK_NODE(n)                                                \
40     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
41
42 /*
43  * Types are private:
44  */
45
46 typedef enum xsltOutputType {
47     XSLT_OUTPUT_XML = 0,
48     XSLT_OUTPUT_HTML,
49     XSLT_OUTPUT_TEXT
50 } xsltOutputType;
51
52 typedef struct _xsltTransformContext xsltTransformContext;
53 typedef xsltTransformContext *xsltTransformContextPtr;
54 struct _xsltTransformContext {
55     xsltStylesheetPtr style;            /* the stylesheet used */
56     xsltOutputType type;                /* the type of output */
57
58     xmlDocPtr doc;                      /* the current doc */
59     xmlNodePtr node;                    /* the current node */
60     xmlNodeSetPtr nodeList;             /* the current node list */
61
62     xmlDocPtr output;                   /* the resulting document */
63     xmlNodePtr insert;                  /* the insertion node */
64
65     xmlXPathContextPtr xpathCtxt;       /* the XPath context */
66 };
67
68 /************************************************************************
69  *                                                                      *
70  *                      
71  *                                                                      *
72  ************************************************************************/
73
74 /**
75  * xsltNewTransformContext:
76  *
77  * Create a new XSLT TransformContext
78  *
79  * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
80  */
81 xsltTransformContextPtr
82 xsltNewTransformContext(void) {
83     xsltTransformContextPtr cur;
84
85     cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
86     if (cur == NULL) {
87         xsltGenericError(xsltGenericErrorContext,
88                 "xsltNewTransformContext : malloc failed\n");
89         return(NULL);
90     }
91     memset(cur, 0, sizeof(xsltTransformContext));
92     return(cur);
93 }
94
95 /**
96  * xsltFreeTransformContext:
97  * @ctxt:  an XSLT parser context
98  *
99  * Free up the memory allocated by @ctxt
100  */
101 void
102 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
103     if (ctxt == NULL)
104         return;
105     if (ctxt->xpathCtxt != NULL)
106         xmlXPathFreeContext(ctxt->xpathCtxt);
107     memset(ctxt, -1, sizeof(xsltTransformContext));
108     xmlFree(ctxt);
109 }
110
111 /************************************************************************
112  *                                                                      *
113  *                      
114  *                                                                      *
115  ************************************************************************/
116
117 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
118 void xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
119                  xmlNodePtr inst);
120 void xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst);
121
122 /**
123  * xsltAttribute:
124  * @ctxt:  a XSLT process context
125  * @node:  the node in the source tree.
126  * @inst:  the xslt attribute node
127  *
128  * Process the xslt attribute node on the source node
129  */
130 void
131 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
132                    xmlNodePtr inst) {
133     xmlChar *prop = NULL;
134     xmlChar *ncname = NULL;
135     xmlChar *prefix = NULL;
136     xmlChar *value = NULL;
137     xmlNsPtr ns = NULL;
138     xmlAttrPtr attr;
139
140
141     if (ctxt->insert == NULL)
142         return;
143     if (ctxt->insert->children != NULL) {
144         xsltGenericError(xsltGenericErrorContext,
145              "xslt:attribute : node has already children\n");
146         return;
147     }
148     prop = xmlGetNsProp(inst, (const xmlChar *)"namespace", XSLT_NAMESPACE);
149     if (prop != NULL) {
150         /* TODO: attribute value template */
151         TODO
152         xmlFree(prop);
153         return;
154     }
155     prop = xmlGetNsProp(inst, (const xmlChar *)"name", XSLT_NAMESPACE);
156     if (prop == NULL) {
157         xsltGenericError(xsltGenericErrorContext,
158              "xslt:attribute : name is missing\n");
159         goto error;
160     }
161
162     ncname = xmlSplitQName2(prop, &prefix);
163     if (ncname == NULL) {
164         ncname = prop;
165         prop = NULL;
166         prefix = NULL;
167     }
168     if (xmlStrEqual(ncname, (const xmlChar *) "xmlns")) {
169         xsltGenericError(xsltGenericErrorContext,
170              "xslt:attribute : xmlns forbidden\n");
171         goto error;
172     }
173     if ((prefix != NULL) && (ns == NULL)) {
174         ns = xmlSearchNs(ctxt->insert->doc, ctxt->insert, prefix);
175         if (ns == NULL) {
176             xsltGenericError(xsltGenericErrorContext,
177                 "no namespace bound to prefix %s\n", prefix);
178         }
179     }
180
181     value = xmlNodeListGetString(inst->doc, inst->children, 1);
182     if (value == NULL) {
183         if (ns) {
184 #if LIBXML_VERSION > 202111
185             attr = xmlSetNsProp(ctxt->insert, ncname, ns->href,
186                                 (const xmlChar *)"");
187 #else
188             xsltGenericError(xsltGenericErrorContext,
189                 "xsl:attribute: recompile against newer libxml version\n");
190             attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)"");
191 #endif
192         } else
193             attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)"");
194     } else {
195         /* TODO: attribute value template */
196         if (ns) {
197 #if LIBXML_VERSION > 202111
198             attr = xmlSetNsProp(ctxt->insert, ncname, ns->href, value);
199 #else
200             xsltGenericError(xsltGenericErrorContext,
201                 "xsl:attribute: recompile against newer libxml version\n");
202             attr = xmlSetProp(ctxt->insert, ncname, value);
203 #endif
204         } else
205             attr = xmlSetProp(ctxt->insert, ncname, value);
206     }
207
208 error:
209     if (prop != NULL)
210         xmlFree(prop);
211     if (ncname != NULL)
212         xmlFree(ncname);
213     if (prefix != NULL)
214         xmlFree(prefix);
215     if (value != NULL)
216         xmlFree(value);
217 }
218
219 /**
220  * xsltValueOf:
221  * @ctxt:  a XSLT process context
222  * @node:  the node in the source tree.
223  * @inst:  the xsltValueOf node
224  *
225  * Process the xsltValueOf node on the source node
226  */
227 void
228 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
229                    xmlNodePtr inst) {
230     xmlChar *prop;
231     int disableEscaping = 0;
232     xmlXPathObjectPtr res, tmp;
233     xmlXPathParserContextPtr xpathParserCtxt;
234     xmlNodePtr copy = NULL;
235
236     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
237         return;
238
239     prop = xmlGetNsProp(inst, (const xmlChar *)"disable-output-escaping",
240                         XSLT_NAMESPACE);
241     if (prop != NULL) {
242         if (xmlStrEqual(prop, (const xmlChar *)"yes"))
243             disableEscaping = 1;
244         else if (xmlStrEqual(prop, (const xmlChar *)"no"))
245             disableEscaping = 0;
246         else 
247             xsltGenericError(xsltGenericErrorContext,
248                  "invalud value %s for disable-output-escaping\n", prop);
249
250         xmlFree(prop);
251         if (disableEscaping) {
252             TODO /* disable-output-escaping */
253         }
254     }
255     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
256     if (prop == NULL) {
257         xsltGenericError(xsltGenericErrorContext,
258              "xsltValueOf: select is not defined\n");
259         return;
260     }
261 #ifdef DEBUG_PROCESS
262     xsltGenericDebug(xsltGenericDebugContext,
263          "xsltValueOf: select %s\n", prop);
264 #endif
265
266     if (ctxt->xpathCtxt == NULL) {
267         xmlXPathInit();
268         ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
269         if (ctxt->xpathCtxt == NULL)
270             goto error;
271     }
272     xpathParserCtxt =
273         xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
274     if (xpathParserCtxt == NULL)
275         goto error;
276     ctxt->xpathCtxt->node = node;
277     valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
278     xmlXPathEvalExpr(xpathParserCtxt);
279     xmlXPathStringFunction(xpathParserCtxt, 1);
280     res = valuePop(xpathParserCtxt);
281     do {
282         tmp = valuePop(xpathParserCtxt);
283         if (tmp != NULL) {
284             xmlXPathFreeObject(tmp);
285         }
286     } while (tmp != NULL);
287     if (res != NULL) {
288         if (res->type == XPATH_STRING) {
289             copy = xmlNewText(res->stringval);
290             if (copy != NULL) {
291                 xmlAddChild(ctxt->insert, copy);
292             }
293         }
294     }
295     if (copy == NULL) {
296         xsltGenericError(xsltGenericErrorContext,
297             "xsltDefaultProcessOneNode: text copy failed\n");
298     }
299 #ifdef DEBUG_PROCESS
300     else
301         xsltGenericDebug(xsltGenericDebugContext,
302              "xsltValueOf: result %s\n", res->stringval);
303 #endif
304 error:
305     if (xpathParserCtxt != NULL) {
306         xmlXPathFreeParserContext(xpathParserCtxt);
307         xpathParserCtxt = NULL;
308     }
309     if (prop != NULL)
310         xmlFree(prop);
311     if (res != NULL)
312         xmlXPathFreeObject(res);
313 }
314
315 /**
316  * xsltCopyNode:
317  * @ctxt:  a XSLT process context
318  * @node:  the element node in the source tree.
319  * @insert:  the parent in the result tree.
320  *
321  * Make a copy of the element node @node
322  * and insert it as last child of @insert
323  *
324  * Returns a pointer to the new node, or NULL in case of error
325  */
326 xmlNodePtr
327 xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
328              xmlNodePtr insert) {
329     xmlNodePtr copy;
330
331     copy = xmlCopyNode(node, 0);
332     copy->doc = ctxt->output;
333     if (copy != NULL) {
334         xmlAddChild(insert, copy);
335         /*
336          * Add namespaces as they are needed
337          */
338         if (node->nsDef != NULL)
339             copy->nsDef = xmlCopyNamespaceList(node->nsDef);
340         if (node->ns != NULL) {
341             /*
342              * optimization, if the namespace is already the
343              * on on the parent node, reuse it directly
344              *
345              * TODO: check possible mess with xmlCopyNamespaceList
346              */
347             if ((insert->type == XML_ELEMENT_NODE) &&
348                 (insert->ns != NULL) && 
349                 (xmlStrEqual(insert->ns->href, node->ns->href))) {
350                 copy->ns = insert->ns;
351             } else {
352                 xmlNsPtr ns;
353
354                 /*
355                  * Look in the output tree if the namespace is
356                  * already in scope.
357                  */
358                 ns = xmlSearchNsByHref(ctxt->output, copy,
359                                        node->ns->href);
360                 if (ns != NULL)
361                     copy->ns = ns;
362                 else {
363                     ns = xmlNewNs(copy, node->ns->href,
364                                   node->ns->prefix);
365                 }
366             }
367         }
368     } else {
369         xsltGenericError(xsltGenericErrorContext,
370                 "xsltCopyNode: copy %s failed\n", node->name);
371     }
372     return(copy);
373 }
374
375 /**
376  * xsltDefaultProcessOneNode:
377  * @ctxt:  a XSLT process context
378  * @node:  the node in the source tree.
379  *
380  * Process the source node with the default built-in template rule:
381  * <xsl:template match="*|/">
382  *   <xsl:apply-templates/>
383  * </xsl:template>
384  *
385  * and
386  *
387  * <xsl:template match="text()|@*">
388  *   <xsl:value-of select="."/>
389  * </xsl:template>
390  *
391  * Note also that namespaces declarations are copied directly:
392  *
393  * the built-in template rule is the only template rule that is applied
394  * for namespace nodes.
395  */
396 void
397 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
398     xmlNodePtr copy;
399     xmlNodePtr delete = NULL;
400
401     switch (node->type) {
402         case XML_DOCUMENT_NODE:
403         case XML_HTML_DOCUMENT_NODE:
404         case XML_ELEMENT_NODE:
405             break;
406         default:
407             return;
408     }
409     node = node->children;
410     while (node != NULL) {
411         switch (node->type) {
412             case XML_DOCUMENT_NODE:
413             case XML_HTML_DOCUMENT_NODE:
414             case XML_ELEMENT_NODE:
415                 xsltProcessOneNode(ctxt, node);
416                 break;
417             case XML_TEXT_NODE:
418                 /* TODO: check the whitespace stripping rules ! */
419                 if ((IS_BLANK_NODE(node)) &&
420                     (node->parent != NULL) &&
421                     (ctxt->style->stripSpaces != NULL)) {
422                     const xmlChar *val;
423
424                     val = (const xmlChar *)
425                           xmlHashLookup(ctxt->style->stripSpaces,
426                                         node->parent->name);
427                     if ((val != NULL) &&
428                         (xmlStrEqual(val, (xmlChar *) "strip"))) {
429                         delete = node;
430                         break;
431                     }
432                 }
433                 /* no break on purpose */
434             case XML_CDATA_SECTION_NODE:
435                 copy = xmlCopyNode(node, 0);
436                 if (copy != NULL) {
437                     xmlAddChild(ctxt->insert, copy);
438                 } else {
439                     xsltGenericError(xsltGenericErrorContext,
440                         "xsltDefaultProcessOneNode: text copy failed\n");
441                 }
442                 break;
443             default:
444 #ifdef DEBUG_PROCESS
445                 xsltGenericDebug(xsltGenericDebugContext,
446                  "xsltDefaultProcessOneNode: skipping node type %d\n",
447                                  node->type);
448 #endif
449                 delete = node;
450         }
451         node = node->next;
452         if (delete != NULL) {
453 #ifdef DEBUG_PROCESS
454             xsltGenericDebug(xsltGenericDebugContext,
455                  "xsltDefaultProcessOneNode: removing ignorable blank node\n");
456 #endif
457             xmlUnlinkNode(delete);
458             xmlFreeNode(delete);
459             delete = NULL;
460         }
461     }
462 }
463
464 /**
465  * xsltApplyTemplates:
466  * @ctxt:  a XSLT process context
467  * @node:  the node in the source tree.
468  * @inst:  the apply-templates node
469  *
470  * Process the apply-templates node on the source node
471  */
472 void
473 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
474                    xmlNodePtr inst) {
475     xmlChar *prop;
476
477     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
478         return;
479
480 #ifdef DEBUG_PROCESS
481     xsltGenericDebug(xsltGenericDebugContext,
482          "xsltApplyTemplates: node: %s\n", node->name);
483 #endif
484     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
485     if (prop != NULL) {
486         TODO
487     } else {
488         xsltDefaultProcessOneNode(ctxt, node);
489     }
490 }
491
492 /**
493  * xsltApplyOneTemplate:
494  * @ctxt:  a XSLT process context
495  * @node:  the node in the source tree.
496  * @list:  the template replacement nodelist
497  *
498  * Process the apply-templates node on the source node
499  */
500 void
501 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
502                      xmlNodePtr list) {
503     xmlNodePtr cur, insert, copy, delete = NULL;
504     xmlNodePtr oldInsert;
505
506     oldInsert = insert = ctxt->insert;
507     /*
508      * Insert all non-XSLT nodes found in the template
509      */
510     cur = list;
511     while (cur != NULL) {
512         /*
513          * test, we must have a valid insertion point
514          */
515         if (insert == NULL) {
516 #ifdef DEBUG_PROCESS
517             xsltGenericDebug(xsltGenericDebugContext,
518                  "xsltApplyOneTemplate: insert == NULL !\n");
519 #endif
520             return;
521         }
522
523         /*
524          * Cleanup of ignorable blank node detected
525          */
526         if (delete != NULL) {
527 #ifdef DEBUG_PROCESS
528             xsltGenericDebug(xsltGenericDebugContext,
529                  "xsltApplyOneTemplate: removing ignorable blank node\n");
530 #endif
531             xmlUnlinkNode(delete);
532             xmlFreeNode(delete);
533             delete = NULL;
534         }
535         if (IS_XSLT_ELEM(cur)) {
536             if (IS_XSLT_NAME(cur, "apply-templates")) {
537                 ctxt->insert = insert;
538                 xsltApplyTemplates(ctxt, node, cur);
539                 ctxt->insert = oldInsert;
540             } else if (IS_XSLT_NAME(cur, "value-of")) {
541                 ctxt->insert = insert;
542                 xsltValueOf(ctxt, node, cur);
543                 ctxt->insert = oldInsert;
544             } else if (IS_XSLT_NAME(cur, "if")) {
545                 ctxt->insert = insert;
546                 xsltIf(ctxt, node, cur);
547                 ctxt->insert = oldInsert;
548             } else if (IS_XSLT_NAME(cur, "for-each")) {
549                 ctxt->insert = insert;
550                 xsltForEach(ctxt, node, cur);
551                 ctxt->insert = oldInsert;
552             } else if (IS_XSLT_NAME(cur, "attribute")) {
553                 ctxt->insert = insert;
554                 xsltAttribute(ctxt, node, cur);
555                 ctxt->insert = oldInsert;
556             } else if (IS_XSLT_NAME(cur, "element")) {
557                 ctxt->insert = insert;
558                 xsltAttribute(ctxt, node, cur);
559                 ctxt->insert = oldInsert;
560             } else {
561 #ifdef DEBUG_PROCESS
562                 xsltGenericDebug(xsltGenericDebugContext,
563                      "xsltApplyOneTemplate: found xslt:%s\n", cur->name);
564 #endif
565                 TODO
566             }
567             goto skip_children;
568         } else if (cur->type == XML_TEXT_NODE) {
569             /*
570              * This text comes from the stylesheet
571              * For stylesheets, the set of whitespace-preserving
572              * element names consists of just xsl:text.
573              */
574             if (!(IS_BLANK_NODE(cur))) {
575 #ifdef DEBUG_PROCESS
576                 xsltGenericDebug(xsltGenericDebugContext,
577                      "xsltApplyOneTemplate: copy text %s\n", cur->content);
578 #endif
579                 copy = xmlCopyNode(cur, 0);
580                 if (copy != NULL) {
581                     xmlAddChild(insert, copy);
582                 } else {
583                     xsltGenericError(xsltGenericErrorContext,
584                             "xsltApplyOneTemplate: text copy failed\n");
585                 }
586             } else {
587                 delete = cur;
588             }
589         } else if (cur->type == XML_ELEMENT_NODE) {
590 #ifdef DEBUG_PROCESS
591             xsltGenericDebug(xsltGenericDebugContext,
592                  "xsltApplyOneTemplate: copy node %s\n", cur->name);
593 #endif
594             copy = xsltCopyNode(ctxt, cur, insert);
595             /*
596              * all the attributes are directly inherited
597              * TODO: Do the substitution of {} XPath expressions !!!
598              */
599             if (cur->properties != NULL)
600                 copy->properties = xmlCopyPropList(copy, cur->properties);
601         }
602
603         /*
604          * Skip to next node, in document order.
605          */
606         if (cur->children != NULL) {
607             if (cur->children->type != XML_ENTITY_DECL) {
608                 cur = cur->children;
609                 if (copy != NULL)
610                     insert = copy;
611                 continue;
612             }
613         }
614 skip_children:
615         if (cur->next != NULL) {
616             cur = cur->next;
617             continue;
618         }
619         
620         do {
621             cur = cur->parent;
622             insert = insert->parent;
623             if (cur == NULL)
624                 break;
625             if (cur == list->parent) {
626                 cur = NULL;
627                 break;
628             }
629             if (cur->next != NULL) {
630                 cur = cur->next;
631                 break;
632             }
633         } while (cur != NULL);
634     }
635 }
636
637 /**
638  * xsltIf:
639  * @ctxt:  a XSLT process context
640  * @node:  the node in the source tree.
641  * @inst:  the xslt if node
642  *
643  * Process the xslt if node on the source node
644  */
645 void
646 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
647                    xmlNodePtr inst) {
648     xmlChar *prop;
649     xmlXPathObjectPtr res, tmp;
650     xmlXPathParserContextPtr xpathParserCtxt;
651     int doit;
652
653     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
654         return;
655
656     prop = xmlGetNsProp(inst, (const xmlChar *)"test", XSLT_NAMESPACE);
657     if (prop == NULL) {
658         xsltGenericError(xsltGenericErrorContext,
659              "xsltIf: test is not defined\n");
660         return;
661     }
662 #ifdef DEBUG_PROCESS
663     xsltGenericDebug(xsltGenericDebugContext,
664          "xsltIf: test %s\n", prop);
665 #endif
666
667     if (ctxt->xpathCtxt == NULL) {
668         xmlXPathInit();
669         ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
670         if (ctxt->xpathCtxt == NULL)
671             goto error;
672     }
673     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
674     if (xpathParserCtxt == NULL)
675         goto error;
676     ctxt->xpathCtxt->node = node;
677     valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
678     xmlXPathEvalExpr(xpathParserCtxt);
679     xmlXPathBooleanFunction(xpathParserCtxt, 1);
680     res = valuePop(xpathParserCtxt);
681     do {
682         tmp = valuePop(xpathParserCtxt);
683         if (tmp != NULL) {
684             xmlXPathFreeObject(tmp);
685         }
686     } while (tmp != NULL);
687
688     if (res != NULL) {
689         if (res->type == XPATH_BOOLEAN)
690             doit = res->boolval;
691         else {
692 #ifdef DEBUG_PROCESS
693             xsltGenericDebug(xsltGenericDebugContext,
694                 "xsltIf: test didn't evaluate to a boolean\n");
695 #endif
696             goto error;
697         }
698     }
699
700 #ifdef DEBUG_PROCESS
701     xsltGenericDebug(xsltGenericDebugContext,
702         "xsltIf: test evaluate to %d\n", doit);
703 #endif
704     if (doit) {
705         xsltApplyOneTemplate(ctxt, ctxt->node, inst->children);
706     }
707
708 error:
709     if (xpathParserCtxt != NULL)
710         xmlXPathFreeParserContext(xpathParserCtxt);
711     if (prop != NULL)
712         xmlFree(prop);
713     if (res != NULL)
714         xmlXPathFreeObject(res);
715 }
716
717 /**
718  * xsltForEach:
719  * @ctxt:  a XSLT process context
720  * @node:  the node in the source tree.
721  * @inst:  the xslt for-each node
722  *
723  * Process the xslt for-each node on the source node
724  */
725 void
726 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
727                    xmlNodePtr inst) {
728     xmlChar *prop;
729     xmlXPathObjectPtr res, tmp;
730     xmlNodePtr replacement;
731     xmlNodeSetPtr list = NULL, oldlist;
732     xmlXPathParserContextPtr xpathParserCtxt;
733     int i, oldProximityPosition, oldContextSize;
734
735     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
736         return;
737
738     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
739     if (prop == NULL) {
740         xsltGenericError(xsltGenericErrorContext,
741              "xsltForEach: select is not defined\n");
742         return;
743     }
744 #ifdef DEBUG_PROCESS
745     xsltGenericDebug(xsltGenericDebugContext,
746          "xsltForEach: select %s\n", prop);
747 #endif
748
749     if (ctxt->xpathCtxt == NULL) {
750         xmlXPathInit();
751         ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
752         if (ctxt->xpathCtxt == NULL)
753             goto error;
754     }
755     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
756     if (xpathParserCtxt == NULL)
757         goto error;
758     ctxt->xpathCtxt->node = node;
759     valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
760     xmlXPathEvalExpr(xpathParserCtxt);
761     res = valuePop(xpathParserCtxt);
762     do {
763         tmp = valuePop(xpathParserCtxt);
764         if (tmp != NULL) {
765             xmlXPathFreeObject(tmp);
766         }
767     } while (tmp != NULL);
768
769     if (res != NULL) {
770         if (res->type == XPATH_NODESET)
771             list = res->nodesetval;
772         else {
773 #ifdef DEBUG_PROCESS
774             xsltGenericDebug(xsltGenericDebugContext,
775                 "xsltForEach: select didn't evaluate to a node list\n");
776 #endif
777             goto error;
778         }
779     }
780
781 #ifdef DEBUG_PROCESS
782     xsltGenericDebug(xsltGenericDebugContext,
783         "xsltForEach: select evaluate to %d nodes\n", list->nodeNr);
784 #endif
785     /* TODO: handle and skip the xsl:sort */
786     replacement = inst->children;
787
788     oldlist = ctxt->nodeList;
789     ctxt->nodeList = list;
790     oldContextSize = ctxt->xpathCtxt->contextSize;
791     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
792     ctxt->xpathCtxt->contextSize = list->nodeNr;
793     for (i = 0;i < list->nodeNr;i++) {
794         ctxt->node = list->nodeTab[i];
795         ctxt->xpathCtxt->proximityPosition = i + 1;
796         xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement);
797     }
798     ctxt->nodeList = oldlist;
799     ctxt->xpathCtxt->contextSize = oldContextSize;
800     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
801
802 error:
803     if (xpathParserCtxt != NULL)
804         xmlXPathFreeParserContext(xpathParserCtxt);
805     if (prop != NULL)
806         xmlFree(prop);
807     if (res != NULL)
808         xmlXPathFreeObject(res);
809 }
810
811 /**
812  * xsltProcessOneNode:
813  * @ctxt:  a XSLT process context
814  * @node:  the node in the source tree.
815  *
816  * Process the source node.
817  */
818 void
819 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
820     xsltTemplatePtr template;
821     template = xsltGetTemplate(ctxt->style, node);
822
823     /*
824      * If no template is found, apply the default rule.
825      */
826     if (template == NULL) {
827 #ifdef DEBUG_PROCESS
828         if (node->type == XML_DOCUMENT_NODE)
829             xsltGenericDebug(xsltGenericDebugContext,
830              "xsltProcessOneNode: no template found for /\n");
831         else 
832             xsltGenericDebug(xsltGenericDebugContext,
833              "xsltProcessOneNode: no template found for %s\n", node->name);
834 #endif
835
836         xsltDefaultProcessOneNode(ctxt, node);
837         return;
838     }
839
840     xsltApplyOneTemplate(ctxt, node, template->content);
841 }
842
843 /**
844  * xsltApplyStylesheet:
845  * @style:  a parsed XSLT stylesheet
846  * @doc:  a parsed XML document
847  *
848  * Apply the stylesheet to the document
849  * NOTE: This may lead to a non-wellformed output XML wise !
850  *
851  * Returns the result document or NULL in case of error
852  */
853 xmlDocPtr
854 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc) {
855     xmlDocPtr res = NULL;
856     xsltTransformContextPtr ctxt = NULL;
857     xmlNodePtr root;
858
859     if ((style == NULL) || (doc == NULL))
860         return(NULL);
861     ctxt = xsltNewTransformContext();
862     if (ctxt == NULL)
863         return(NULL);
864     ctxt->doc = doc;
865     ctxt->style = style;
866     if ((style->method != NULL) &&
867         (!xmlStrEqual(style->method, (const xmlChar *) "xml"))) {
868         if (xmlStrEqual(style->method, (const xmlChar *) "html")) {
869             ctxt->type = XSLT_OUTPUT_HTML;
870             res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
871             if (res == NULL)
872                 goto error;
873         } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
874             ctxt->type = XSLT_OUTPUT_TEXT;
875             res = xmlNewDoc(style->version);
876             if (res == NULL)
877                 goto error;
878         } else {
879             xsltGenericError(xsltGenericErrorContext,
880                              "xsltApplyStylesheet: insupported method %s\n",
881                              style->method);
882             goto error;
883         }
884     } else {
885         ctxt->type = XSLT_OUTPUT_XML;
886         res = xmlNewDoc(style->version);
887         if (res == NULL)
888             goto error;
889     }
890     res->charset = XML_CHAR_ENCODING_UTF8;
891     if (style->encoding != NULL)
892         res->encoding = xmlStrdup(style->encoding);
893
894     /*
895      * Start.
896      */
897     ctxt->output = res;
898     ctxt->insert = (xmlNodePtr) res;
899     ctxt->node = (xmlNodePtr) doc;
900     xsltProcessOneNode(ctxt, ctxt->node);
901
902
903     if ((ctxt->type = XSLT_OUTPUT_XML) &&
904         ((style->doctypePublic != NULL) ||
905          (style->doctypeSystem != NULL))) {
906         root = xmlDocGetRootElement(res);
907         if (root != NULL)
908             res->intSubset = xmlCreateIntSubset(res, root->name,
909                          style->doctypePublic, style->doctypeSystem);
910     }
911     xmlXPathFreeNodeSet(ctxt->nodeList);
912     xsltFreeTransformContext(ctxt);
913     return(res);
914
915 error:
916     if (res != NULL)
917         xmlFreeDoc(res);
918     if (ctxt != NULL)
919         xsltFreeTransformContext(ctxt);
920     return(NULL);
921 }
922