665d1ea39618cad9c46017c48fc4cafa52547778
[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 #include "variables.h"
33 #include "namespaces.h"
34 #include "templates.h"
35
36 #define DEBUG_PROCESS
37
38 /*
39  * Useful macros
40  */
41
42 #define IS_BLANK_NODE(n)                                                \
43     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
44
45
46 /************************************************************************
47  *                                                                      *
48  *                      
49  *                                                                      *
50  ************************************************************************/
51
52 /**
53  * xsltNewTransformContext:
54  *
55  * Create a new XSLT TransformContext
56  *
57  * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
58  */
59 xsltTransformContextPtr
60 xsltNewTransformContext(void) {
61     xsltTransformContextPtr cur;
62
63     cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
64     if (cur == NULL) {
65         xsltGenericError(xsltGenericErrorContext,
66                 "xsltNewTransformContext : malloc failed\n");
67         return(NULL);
68     }
69     memset(cur, 0, sizeof(xsltTransformContext));
70     return(cur);
71 }
72
73 /**
74  * xsltFreeTransformContext:
75  * @ctxt:  an XSLT parser context
76  *
77  * Free up the memory allocated by @ctxt
78  */
79 void
80 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
81     xmlDocPtr doc, next;
82
83     if (ctxt == NULL)
84         return;
85     doc = ctxt->extraDocs;
86     while (doc != NULL) {
87         next = (xmlDocPtr) doc->next;
88         xmlFreeDoc(doc);
89         doc = next;
90     }
91     if (ctxt->xpathCtxt != NULL)
92         xmlXPathFreeContext(ctxt->xpathCtxt);
93     xsltFreeVariableHashes(ctxt);
94     memset(ctxt, -1, sizeof(xsltTransformContext));
95     xmlFree(ctxt);
96 }
97
98 /************************************************************************
99  *                                                                      *
100  *                      
101  *                                                                      *
102  ************************************************************************/
103
104 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
105 void xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
106                  xmlNodePtr inst);
107 void xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst);
108
109 /**
110  * xsltSort:
111  * @ctxt:  a XSLT process context
112  * @node:  the node in the source tree.
113  * @inst:  the xslt sort node
114  *
115  * Process the xslt sort node on the source node
116  */
117 void
118 xsltSort(xsltTransformContextPtr ctxt, xmlNodePtr node,
119                    xmlNodePtr inst) {
120     xmlXPathObjectPtr *results = NULL;
121     xmlNodeSetPtr list = NULL;
122     xmlXPathParserContextPtr xpathParserCtxt = NULL;
123     xmlChar *prop = NULL;
124     xmlXPathObjectPtr res, tmp;
125     const xmlChar *start;
126     int descending = 0;
127     int number = 0;
128     int len = 0;
129     int i;
130
131     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
132         return;
133
134     list = ctxt->nodeList;
135     if ((list == NULL) || (list->nodeNr <= 1))
136         goto error; /* nothing to do */
137
138     len = list->nodeNr;
139
140     /* TODO: process attributes as attribute value templates */
141     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"data-type");
142     if (prop != NULL) {
143         if (xmlStrEqual(prop, (const xmlChar *) "text"))
144             number = 0;
145         else if (xmlStrEqual(prop, (const xmlChar *) "number"))
146             number = 1;
147         else {
148             xsltGenericError(xsltGenericErrorContext,
149                  "xsltSort: no support for data-type = %s\n", prop);
150             goto error;
151         }
152         xmlFree(prop);
153     }
154     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"order");
155     if (prop != NULL) {
156         if (xmlStrEqual(prop, (const xmlChar *) "ascending"))
157             descending = 0;
158         else if (xmlStrEqual(prop, (const xmlChar *) "descending"))
159             descending = 1;
160         else {
161             xsltGenericError(xsltGenericErrorContext,
162                  "xsltSort: invalid value %s for order\n", prop);
163             goto error;
164         }
165         xmlFree(prop);
166     }
167     /* TODO: xsl:sort lang attribute */
168     /* TODO: xsl:sort case-order attribute */
169
170     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
171     if (prop == NULL) {
172         prop = xmlNodeGetContent(inst);
173         if (prop == NULL) {
174             xsltGenericError(xsltGenericErrorContext,
175                  "xsltSort: select is not defined\n");
176             return;
177         }
178     }
179
180     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
181     if (xpathParserCtxt == NULL)
182         goto error;
183     results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
184     if (results == NULL) {
185         xsltGenericError(xsltGenericErrorContext,
186              "xsltSort: memory allocation failure\n");
187         goto error;
188     }
189
190     start = xpathParserCtxt->cur;
191     for (i = 0;i < len;i++) {
192         xpathParserCtxt->cur = start;
193         node = ctxt->node = list->nodeTab[i];
194         ctxt->xpathCtxt->proximityPosition = i + 1;
195         valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
196         xmlXPathEvalExpr(xpathParserCtxt);
197         xmlXPathStringFunction(xpathParserCtxt, 1);
198         if (number)
199             xmlXPathNumberFunction(xpathParserCtxt, 1);
200         res = valuePop(xpathParserCtxt);
201         do {
202             tmp = valuePop(xpathParserCtxt);
203             if (tmp != NULL) {
204                 xmlXPathFreeObject(tmp);
205             }
206         } while (tmp != NULL);
207
208         if (res != NULL) {
209             if (number) {
210                 if (res->type == XPATH_NUMBER) {
211                     results[i] = res;
212                 } else {
213 #ifdef DEBUG_PROCESS
214                     xsltGenericDebug(xsltGenericDebugContext,
215                         "xsltSort: select didn't evaluate to a number\n");
216 #endif
217                     results[i] = NULL;
218                 }
219             } else {
220                 if (res->type == XPATH_STRING) {
221                     results[i] = res;
222                 } else {
223 #ifdef DEBUG_PROCESS
224                     xsltGenericDebug(xsltGenericDebugContext,
225                         "xsltSort: select didn't evaluate to a string\n");
226 #endif
227                     results[i] = NULL;
228                 }
229             }
230         }
231     }
232
233     xsltSortFunction(list, &results[0], descending, number);
234
235 error:
236     if (xpathParserCtxt != NULL)
237         xmlXPathFreeParserContext(xpathParserCtxt);
238     if (prop != NULL)
239         xmlFree(prop);
240     if (results != NULL) {
241         for (i = 0;i < len;i++)
242             xmlXPathFreeObject(results[i]);
243         xmlFree(results);
244     }
245 }
246
247 /**
248  * xsltComment:
249  * @ctxt:  a XSLT process context
250  * @node:  the node in the source tree.
251  * @inst:  the xslt comment node
252  *
253  * Process the xslt comment node on the source node
254  */
255 void
256 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
257                    xmlNodePtr inst) {
258     xmlChar *value = NULL;
259     xmlNodePtr comment;
260
261     value = xsltEvalTemplateString(ctxt, node, inst);
262     /* TODO: check that there is no -- sequence and doesn't end up with - */
263 #ifdef DEBUG_PROCESS
264     if (value == NULL)
265         xsltGenericDebug(xsltGenericDebugContext,
266              "xsl:comment: empty\n");
267     else
268         xsltGenericDebug(xsltGenericDebugContext,
269              "xsl:comment: content %s\n", value);
270 #endif
271
272     comment = xmlNewComment(value);
273     xmlAddChild(ctxt->insert, comment);
274
275     if (value != NULL)
276         xmlFree(value);
277 }
278
279 /**
280  * xsltProcessingInstruction:
281  * @ctxt:  a XSLT process context
282  * @node:  the node in the source tree.
283  * @inst:  the xslt processing-instruction node
284  *
285  * Process the xslt processing-instruction node on the source node
286  */
287 void
288 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
289                    xmlNodePtr inst) {
290     xmlChar *ncname = NULL;
291     xmlChar *value = NULL;
292     xmlNodePtr pi;
293
294
295     if (ctxt->insert == NULL)
296         return;
297     ncname = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
298     if (ncname == NULL) {
299         xsltGenericError(xsltGenericErrorContext,
300              "xslt:processing-instruction : name is missing\n");
301         goto error;
302     }
303     /* TODO: check that it's both an an NCName and a PITarget. */
304
305
306     value = xsltEvalTemplateString(ctxt, node, inst);
307     /* TODO: check that there is no ?> sequence */
308 #ifdef DEBUG_PROCESS
309     if (value == NULL)
310         xsltGenericDebug(xsltGenericDebugContext,
311              "xsl:processing-instruction: %s empty\n", ncname);
312     else
313         xsltGenericDebug(xsltGenericDebugContext,
314              "xsl:processing-instruction: %s content %s\n", ncname, value);
315 #endif
316
317     pi = xmlNewPI(ncname, value);
318     xmlAddChild(ctxt->insert, pi);
319
320 error:
321     if (ncname != NULL)
322         xmlFree(ncname);
323     if (value != NULL)
324         xmlFree(value);
325 }
326
327 /**
328  * xsltAttribute:
329  * @ctxt:  a XSLT process context
330  * @node:  the node in the source tree.
331  * @inst:  the xslt attribute node
332  *
333  * Process the xslt attribute node on the source node
334  */
335 void
336 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
337                    xmlNodePtr inst) {
338     xmlChar *prop = NULL;
339     xmlChar *ncname = NULL;
340     xmlChar *prefix = NULL;
341     xmlChar *value = NULL;
342     xmlNsPtr ns = NULL;
343     xmlAttrPtr attr;
344
345
346     if (ctxt->insert == NULL)
347         return;
348     if (ctxt->insert->children != NULL) {
349         xsltGenericError(xsltGenericErrorContext,
350              "xslt:attribute : node has already children\n");
351         return;
352     }
353     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
354     if (prop == NULL) {
355         xsltGenericError(xsltGenericErrorContext,
356              "xslt:attribute : name is missing\n");
357         goto error;
358     }
359
360     ncname = xmlSplitQName2(prop, &prefix);
361     if (ncname == NULL) {
362         ncname = prop;
363         prop = NULL;
364         prefix = NULL;
365     }
366     if (xmlStrEqual(ncname, (const xmlChar *) "xmlns")) {
367         xsltGenericError(xsltGenericErrorContext,
368              "xslt:attribute : xmlns forbidden\n");
369         goto error;
370     }
371     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"namespace");
372     if (prop != NULL) {
373         TODO /* xsl:attribute namespace */
374         xmlFree(prop);
375         return;
376     } else {
377         if (prefix != NULL) {
378             ns = xmlSearchNs(inst->doc, inst, prefix);
379             if (ns == NULL) {
380                 xsltGenericError(xsltGenericErrorContext,
381                     "no namespace bound to prefix %s\n", prefix);
382             } else {
383                 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
384             }
385         }
386     }
387     
388
389     value = xsltEvalTemplateString(ctxt, node, inst);
390     if (value == NULL) {
391         if (ns) {
392             attr = xmlSetNsProp(ctxt->insert, ns, ncname, 
393                                 (const xmlChar *)"");
394         } else
395             attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)"");
396     } else {
397         if (ns) {
398             attr = xmlSetNsProp(ctxt->insert, ns, ncname, value);
399         } else
400             attr = xmlSetProp(ctxt->insert, ncname, value);
401         
402     }
403
404 error:
405     if (prop != NULL)
406         xmlFree(prop);
407     if (ncname != NULL)
408         xmlFree(ncname);
409     if (prefix != NULL)
410         xmlFree(prefix);
411     if (value != NULL)
412         xmlFree(value);
413 }
414
415 /**
416  * xsltValueOf:
417  * @ctxt:  a XSLT process context
418  * @node:  the node in the source tree.
419  * @inst:  the xsltValueOf node
420  *
421  * Process the xsltValueOf node on the source node
422  */
423 void
424 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
425                    xmlNodePtr inst) {
426     xmlChar *prop;
427     int disableEscaping = 0;
428     xmlXPathObjectPtr res = NULL, tmp;
429     xmlXPathParserContextPtr xpathParserCtxt = NULL;
430     xmlNodePtr copy = NULL;
431
432     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
433         return;
434
435     prop = xmlGetNsProp(inst, (const xmlChar *)"disable-output-escaping",
436                         XSLT_NAMESPACE);
437     if (prop != NULL) {
438         if (xmlStrEqual(prop, (const xmlChar *)"yes"))
439             disableEscaping = 1;
440         else if (xmlStrEqual(prop, (const xmlChar *)"no"))
441             disableEscaping = 0;
442         else 
443             xsltGenericError(xsltGenericErrorContext,
444                  "invalud value %s for disable-output-escaping\n", prop);
445
446         xmlFree(prop);
447         if (disableEscaping) {
448             TODO /* disable-output-escaping */
449         }
450     }
451     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
452     if (prop == NULL) {
453         xsltGenericError(xsltGenericErrorContext,
454              "xsltValueOf: select is not defined\n");
455         return;
456     }
457 #ifdef DEBUG_PROCESS
458     xsltGenericDebug(xsltGenericDebugContext,
459          "xsltValueOf: select %s\n", prop);
460 #endif
461
462     if (ctxt->xpathCtxt == NULL) {
463         xmlXPathInit();
464         ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
465         if (ctxt->xpathCtxt == NULL)
466             goto error;
467         XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
468     }
469     xpathParserCtxt =
470         xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
471     if (xpathParserCtxt == NULL)
472         goto error;
473     ctxt->xpathCtxt->node = node;
474     valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
475     xmlXPathEvalExpr(xpathParserCtxt);
476     xmlXPathStringFunction(xpathParserCtxt, 1);
477     res = valuePop(xpathParserCtxt);
478     do {
479         tmp = valuePop(xpathParserCtxt);
480         if (tmp != NULL) {
481             xmlXPathFreeObject(tmp);
482         }
483     } while (tmp != NULL);
484     if (res != NULL) {
485         if (res->type == XPATH_STRING) {
486             copy = xmlNewText(res->stringval);
487             if (copy != NULL) {
488                 xmlAddChild(ctxt->insert, copy);
489             }
490         }
491     }
492     if (copy == NULL) {
493         xsltGenericError(xsltGenericErrorContext,
494             "xsltDefaultProcessOneNode: text copy failed\n");
495     }
496 #ifdef DEBUG_PROCESS
497     else
498         xsltGenericDebug(xsltGenericDebugContext,
499              "xsltValueOf: result %s\n", res->stringval);
500 #endif
501 error:
502     if (xpathParserCtxt != NULL) {
503         xmlXPathFreeParserContext(xpathParserCtxt);
504         xpathParserCtxt = NULL;
505     }
506     if (prop != NULL)
507         xmlFree(prop);
508     if (res != NULL)
509         xmlXPathFreeObject(res);
510 }
511
512 /**
513  * xsltCopyNode:
514  * @ctxt:  a XSLT process context
515  * @node:  the element node in the source tree.
516  * @insert:  the parent in the result tree.
517  *
518  * Make a copy of the element node @node
519  * and insert it as last child of @insert
520  *
521  * Returns a pointer to the new node, or NULL in case of error
522  */
523 xmlNodePtr
524 xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
525              xmlNodePtr insert) {
526     xmlNodePtr copy;
527
528     copy = xmlCopyNode(node, 0);
529     copy->doc = ctxt->output;
530     if (copy != NULL) {
531         xmlAddChild(insert, copy);
532         /*
533          * Add namespaces as they are needed
534          */
535         if (node->nsDef != NULL)
536             xsltCopyNamespaceList(ctxt, copy, node->nsDef);
537         if (node->ns != NULL) {
538             copy->ns = xsltGetNamespace(ctxt, node, node->ns, insert);
539         }
540     } else {
541         xsltGenericError(xsltGenericErrorContext,
542                 "xsltCopyNode: copy %s failed\n", node->name);
543     }
544     return(copy);
545 }
546
547 /**
548  * xsltDefaultProcessOneNode:
549  * @ctxt:  a XSLT process context
550  * @node:  the node in the source tree.
551  *
552  * Process the source node with the default built-in template rule:
553  * <xsl:template match="*|/">
554  *   <xsl:apply-templates/>
555  * </xsl:template>
556  *
557  * and
558  *
559  * <xsl:template match="text()|@*">
560  *   <xsl:value-of select="."/>
561  * </xsl:template>
562  *
563  * Note also that namespaces declarations are copied directly:
564  *
565  * the built-in template rule is the only template rule that is applied
566  * for namespace nodes.
567  */
568 void
569 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
570     xmlNodePtr copy;
571     xmlNodePtr delete = NULL;
572     int strip_spaces = -1;
573
574     CHECK_STOPPED;
575     switch (node->type) {
576         case XML_DOCUMENT_NODE:
577         case XML_HTML_DOCUMENT_NODE:
578         case XML_ELEMENT_NODE:
579             break;
580         case XML_TEXT_NODE:
581             copy = xmlCopyNode(node, 0);
582             if (copy != NULL) {
583                 xmlAddChild(ctxt->insert, copy);
584             } else {
585                 xsltGenericError(xsltGenericErrorContext,
586                     "xsltDefaultProcessOneNode: text copy failed\n");
587             }
588             return;
589         default:
590             return;
591     }
592     node = node->children;
593     while (node != NULL) {
594         switch (node->type) {
595             case XML_DOCUMENT_NODE:
596             case XML_HTML_DOCUMENT_NODE:
597             case XML_ELEMENT_NODE:
598                 xsltProcessOneNode(ctxt, node);
599                 break;
600             case XML_TEXT_NODE:
601                 /* TODO: check the whitespace stripping rules ! */
602                 if ((IS_BLANK_NODE(node)) &&
603                     (node->parent != NULL) &&
604                     (ctxt->style->stripSpaces != NULL)) {
605                     const xmlChar *val;
606
607                     if (strip_spaces == -1) {
608                         /* TODO: add namespaces support */
609                         val = (const xmlChar *)
610                               xmlHashLookup(ctxt->style->stripSpaces,
611                                             node->parent->name);
612                         if (val != NULL) {
613                             if (xmlStrEqual(val, (xmlChar *) "strip"))
614                                 strip_spaces = 1;
615                             if (xmlStrEqual(val, (xmlChar *) "preserve"))
616                                 strip_spaces = 0;
617                         } 
618                         if (strip_spaces == -1) {
619                             val = (const xmlChar *)
620                                   xmlHashLookup(ctxt->style->stripSpaces,
621                                                 (const xmlChar *)"*");
622                             if ((val != NULL) &&
623                                 (xmlStrEqual(val, (xmlChar *) "strip")))
624                                 strip_spaces = 1;
625                             else
626                                 strip_spaces = 0;
627                         }
628                     }
629                     if (strip_spaces == 1) {
630                         delete = node;
631                         break;
632                     }
633                 }
634                 /* no break on purpose */
635             case XML_CDATA_SECTION_NODE:
636                 copy = xmlCopyNode(node, 0);
637                 if (copy != NULL) {
638                     xmlAddChild(ctxt->insert, copy);
639                 } else {
640                     xsltGenericError(xsltGenericErrorContext,
641                         "xsltDefaultProcessOneNode: text copy failed\n");
642                 }
643                 break;
644             default:
645 #ifdef DEBUG_PROCESS
646                 xsltGenericDebug(xsltGenericDebugContext,
647                  "xsltDefaultProcessOneNode: skipping node type %d\n",
648                                  node->type);
649 #endif
650                 delete = node;
651         }
652         node = node->next;
653         if (delete != NULL) {
654 #ifdef DEBUG_PROCESS
655             xsltGenericDebug(xsltGenericDebugContext,
656                  "xsltDefaultProcessOneNode: removing ignorable blank node\n");
657 #endif
658             xmlUnlinkNode(delete);
659             xmlFreeNode(delete);
660             delete = NULL;
661         }
662     }
663 }
664
665 /**
666  * xsltCallTemplate:
667  * @ctxt:  a XSLT process context
668  * @node:  the node in the source tree.
669  * @inst:  the xslt call-template node
670  *
671  * Process the xslt call-template node on the source node
672  */
673 void
674 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
675                    xmlNodePtr inst) {
676     xmlChar *prop = NULL;
677     xmlChar *ncname = NULL;
678     xmlChar *prefix = NULL;
679     xmlNsPtr ns = NULL;
680     xsltTemplatePtr template;
681     xmlNodePtr cur = NULL;
682     int has_param = 0;
683
684
685     if (ctxt->insert == NULL)
686         return;
687     prop = xmlGetNsProp(inst, (const xmlChar *)"name", XSLT_NAMESPACE);
688     if (prop == NULL) {
689         xsltGenericError(xsltGenericErrorContext,
690              "xslt:call-template : name is missing\n");
691         goto error;
692     }
693
694     ncname = xmlSplitQName2(prop, &prefix);
695     if (ncname == NULL) {
696         ncname = prop;
697         prop = NULL;
698         prefix = NULL;
699     }
700     if ((prefix != NULL) && (ns == NULL)) {
701         ns = xmlSearchNs(ctxt->insert->doc, ctxt->insert, prefix);
702         if (ns == NULL) {
703             xsltGenericError(xsltGenericErrorContext,
704                 "no namespace bound to prefix %s\n", prefix);
705         }
706     }
707     if (ns != NULL)
708         template = xsltFindTemplate(ctxt->style, ncname, ns->href);
709     else
710         template = xsltFindTemplate(ctxt->style, ncname, NULL);
711     if (template == NULL) {
712         xsltGenericError(xsltGenericDebugContext,
713              "xslt:call-template: template %s not found\n", cur->name);
714         goto error;
715     }
716     cur = inst->children;
717     while (cur != NULL) {
718         CHECK_STOPPEDE;
719         if (IS_XSLT_ELEM(cur)) {
720             if (IS_XSLT_NAME(cur, "with-param")) {
721                 if (has_param == 0) {
722                     xsltPushStack(ctxt);
723                     has_param = 1;
724                 }
725                 xsltParseStylesheetParam(ctxt, cur);
726             } else {
727                 xsltGenericError(xsltGenericDebugContext,
728                      "xslt:call-template: misplaced xslt:%s\n", cur->name);
729             }
730         } else {
731             xsltGenericError(xsltGenericDebugContext,
732                  "xslt:call-template: misplaced %s element\n", cur->name);
733         }
734         cur = cur->next;
735     }
736     xsltApplyOneTemplate(ctxt, node, template->content);
737
738 error:
739     if (has_param == 1)
740         xsltPopStack(ctxt);
741     if (prop != NULL)
742         xmlFree(prop);
743     if (ncname != NULL)
744         xmlFree(ncname);
745     if (prefix != NULL)
746         xmlFree(prefix);
747 }
748
749 /**
750  * xsltApplyTemplates:
751  * @ctxt:  a XSLT process context
752  * @node:  the node in the source tree.
753  * @inst:  the apply-templates node
754  *
755  * Process the apply-templates node on the source node
756  */
757 void
758 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
759                    xmlNodePtr inst) {
760     xmlChar *prop = NULL;
761     xmlNodePtr cur, delete = NULL;
762     xmlXPathObjectPtr res = NULL, tmp;
763     xmlNodePtr replacement;
764     xmlNodeSetPtr list = NULL, oldlist;
765     xmlXPathParserContextPtr xpathParserCtxt = NULL;
766     int i, oldProximityPosition, oldContextSize;
767
768     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
769         return;
770
771 #ifdef DEBUG_PROCESS
772     xsltGenericDebug(xsltGenericDebugContext,
773          "xsltApplyTemplates: node: %s\n", node->name);
774 #endif
775     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
776     if (prop != NULL) {
777 #ifdef DEBUG_PROCESS
778         xsltGenericDebug(xsltGenericDebugContext,
779              "xsltApplyTemplates: select %s\n", prop);
780 #endif
781
782         if (ctxt->xpathCtxt == NULL) {
783             xmlXPathInit();
784             ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
785             if (ctxt->xpathCtxt == NULL)
786                 goto error;
787             XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
788         }
789         xpathParserCtxt =
790             xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
791         if (xpathParserCtxt == NULL)
792             goto error;
793         ctxt->xpathCtxt->node = node;
794         valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
795         xmlXPathEvalExpr(xpathParserCtxt);
796         res = valuePop(xpathParserCtxt);
797         do {
798             tmp = valuePop(xpathParserCtxt);
799             if (tmp != NULL) {
800                 xmlXPathFreeObject(tmp);
801             }
802         } while (tmp != NULL);
803         if (res != NULL) {
804             if (res->type == XPATH_NODESET) {
805                 list = res->nodesetval;
806                 res->nodesetval = NULL;
807              } else {
808 #ifdef DEBUG_PROCESS
809                 xsltGenericDebug(xsltGenericDebugContext,
810                     "xsltApplyTemplates: select didn't evaluate to a node list\n");
811 #endif
812                 goto error;
813             }
814         }
815     } else {
816         /*
817          * Build an XPath nodelist with the children
818          */
819         list = xmlXPathNodeSetCreate(NULL);
820         cur = node->children;
821         while (cur != NULL) {
822             switch (cur->type) {
823                 case XML_TEXT_NODE:
824                     /* TODO: check the whitespace stripping rules ! */
825                     if ((IS_BLANK_NODE(cur)) &&
826                         (cur->parent != NULL) &&
827                         (ctxt->style->stripSpaces != NULL)) {
828                         const xmlChar *val;
829
830                         val = (const xmlChar *)
831                               xmlHashLookup(ctxt->style->stripSpaces,
832                                             cur->parent->name);
833                         if ((val != NULL) &&
834                             (xmlStrEqual(val, (xmlChar *) "strip"))) {
835                             delete = cur;
836                             break;
837                         }
838                     }
839                     /* no break on purpose */
840                 case XML_DOCUMENT_NODE:
841                 case XML_HTML_DOCUMENT_NODE:
842                 case XML_ELEMENT_NODE:
843                 case XML_CDATA_SECTION_NODE:
844                     xmlXPathNodeSetAdd(list, cur);
845                     break;
846                 default:
847 #ifdef DEBUG_PROCESS
848                     xsltGenericDebug(xsltGenericDebugContext,
849                      "xsltApplyTemplates: skipping cur type %d\n",
850                                      cur->type);
851 #endif
852                     delete = cur;
853             }
854             cur = cur->next;
855             if (delete != NULL) {
856 #ifdef DEBUG_PROCESS
857                 xsltGenericDebug(xsltGenericDebugContext,
858                      "xsltApplyTemplates: removing ignorable blank cur\n");
859 #endif
860                 xmlUnlinkNode(delete);
861                 xmlFreeNode(delete);
862                 delete = NULL;
863             }
864         }
865     }
866
867 #ifdef DEBUG_PROCESS
868     xsltGenericDebug(xsltGenericDebugContext,
869         "xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
870 #endif
871
872     oldlist = ctxt->nodeList;
873     ctxt->nodeList = list;
874     oldContextSize = ctxt->xpathCtxt->contextSize;
875     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
876     ctxt->xpathCtxt->contextSize = list->nodeNr;
877
878     /* 
879      * handle and skip the xsl:sort
880      */
881     replacement = inst->children;
882     while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
883         xsltSort(ctxt, node, replacement);
884         replacement = replacement->next;
885     }
886
887     for (i = 0;i < list->nodeNr;i++) {
888         ctxt->node = list->nodeTab[i];
889         ctxt->xpathCtxt->proximityPosition = i + 1;
890         xsltProcessOneNode(ctxt, list->nodeTab[i]);
891     }
892     ctxt->nodeList = oldlist;
893     ctxt->xpathCtxt->contextSize = oldContextSize;
894     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
895
896 error:
897     if (xpathParserCtxt != NULL)
898         xmlXPathFreeParserContext(xpathParserCtxt);
899     if (prop != NULL)
900         xmlFree(prop);
901     if (res != NULL)
902         xmlXPathFreeObject(res);
903     if (list != NULL)
904         xmlXPathFreeNodeSet(list);
905 }
906
907 /**
908  * xsltApplyOneTemplate:
909  * @ctxt:  a XSLT process context
910  * @node:  the node in the source tree.
911  * @list:  the template replacement nodelist
912  *
913  * Process the apply-templates node on the source node
914  */
915 void
916 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
917                      xmlNodePtr list) {
918     xmlNodePtr cur = NULL, insert, copy = NULL;
919     xmlNodePtr oldInsert;
920     int has_variables = 0;
921
922     CHECK_STOPPED;
923     oldInsert = insert = ctxt->insert;
924     /*
925      * Insert all non-XSLT nodes found in the template
926      */
927     cur = list;
928     while (cur != NULL) {
929         /*
930          * test, we must have a valid insertion point
931          */
932         if (insert == NULL) {
933 #ifdef DEBUG_PROCESS
934             xsltGenericDebug(xsltGenericDebugContext,
935                  "xsltApplyOneTemplate: insert == NULL !\n");
936 #endif
937             return;
938         }
939
940         if (IS_XSLT_ELEM(cur)) {
941             if (IS_XSLT_NAME(cur, "apply-templates")) {
942                 ctxt->insert = insert;
943                 xsltApplyTemplates(ctxt, node, cur);
944                 ctxt->insert = oldInsert;
945             } else if (IS_XSLT_NAME(cur, "value-of")) {
946                 ctxt->insert = insert;
947                 xsltValueOf(ctxt, node, cur);
948                 ctxt->insert = oldInsert;
949             } else if (IS_XSLT_NAME(cur, "if")) {
950                 ctxt->insert = insert;
951                 xsltIf(ctxt, node, cur);
952                 ctxt->insert = oldInsert;
953             } else if (IS_XSLT_NAME(cur, "for-each")) {
954                 ctxt->insert = insert;
955                 xsltForEach(ctxt, node, cur);
956                 ctxt->insert = oldInsert;
957             } else if (IS_XSLT_NAME(cur, "attribute")) {
958                 ctxt->insert = insert;
959                 xsltAttribute(ctxt, node, cur);
960                 ctxt->insert = oldInsert;
961                 /*******
962             } else if (IS_XSLT_NAME(cur, "element")) {
963                 ctxt->insert = insert;
964                 xsltElement(ctxt, node, cur);
965                 ctxt->insert = oldInsert;
966                 *******/
967             } else if (IS_XSLT_NAME(cur, "comment")) {
968                 ctxt->insert = insert;
969                 xsltComment(ctxt, node, cur);
970                 ctxt->insert = oldInsert;
971             } else if (IS_XSLT_NAME(cur, "processing-instruction")) {
972                 ctxt->insert = insert;
973                 xsltProcessingInstruction(ctxt, node, cur);
974                 ctxt->insert = oldInsert;
975             } else if (IS_XSLT_NAME(cur, "variable")) {
976                 if (has_variables == 0) {
977                     xsltPushStack(ctxt);
978                     has_variables = 1;
979                 }
980                 xsltParseStylesheetVariable(ctxt, cur);
981             } else if (IS_XSLT_NAME(cur, "param")) {
982                 if (has_variables == 0) {
983                     xsltPushStack(ctxt);
984                     has_variables = 1;
985                 }
986                 xsltParseStylesheetParam(ctxt, cur);
987             } else if (IS_XSLT_NAME(cur, "call-template")) {
988                 if (has_variables == 0) {
989                     xsltPushStack(ctxt);
990                     has_variables = 1;
991                 }
992                 xsltCallTemplate(ctxt, node, cur);
993             } else if (IS_XSLT_NAME(cur, "message")) {
994             } else {
995 #ifdef DEBUG_PROCESS
996                 xsltGenericError(xsltGenericDebugContext,
997                      "xsltApplyOneTemplate: found xslt:%s\n", cur->name);
998 #endif
999                 TODO
1000             }
1001             CHECK_STOPPED;
1002             goto skip_children;
1003         } else if (cur->type == XML_TEXT_NODE) {
1004             /*
1005              * This text comes from the stylesheet
1006              * For stylesheets, the set of whitespace-preserving
1007              * element names consists of just xsl:text.
1008              */
1009 #ifdef DEBUG_PROCESS
1010             xsltGenericDebug(xsltGenericDebugContext,
1011                  "xsltApplyOneTemplate: copy text %s\n", cur->content);
1012 #endif
1013             copy = xmlCopyNode(cur, 0);
1014             if (copy != NULL) {
1015                 xmlAddChild(insert, copy);
1016             } else {
1017                 xsltGenericError(xsltGenericErrorContext,
1018                         "xsltApplyOneTemplate: text copy failed\n");
1019             }
1020         } else if (cur->type == XML_ELEMENT_NODE) {
1021 #ifdef DEBUG_PROCESS
1022             xsltGenericDebug(xsltGenericDebugContext,
1023                  "xsltApplyOneTemplate: copy node %s\n", cur->name);
1024 #endif
1025             copy = xsltCopyNode(ctxt, cur, insert);
1026             /*
1027              * all the attributes are directly inherited
1028              * TODO: Do the substitution of {} XPath expressions !!!
1029              */
1030             if (cur->properties != NULL)
1031                 copy->properties = xsltAttrListTemplateProcess(ctxt,
1032                                                copy, cur->properties);
1033         }
1034
1035         /*
1036          * Skip to next node, in document order.
1037          */
1038         if (cur->children != NULL) {
1039             if (cur->children->type != XML_ENTITY_DECL) {
1040                 cur = cur->children;
1041                 if (copy != NULL)
1042                     insert = copy;
1043                 continue;
1044             }
1045         }
1046 skip_children:
1047         if (cur->next != NULL) {
1048             cur = cur->next;
1049             continue;
1050         }
1051         
1052         do {
1053             cur = cur->parent;
1054             insert = insert->parent;
1055             if (cur == NULL)
1056                 break;
1057             if (cur == list->parent) {
1058                 cur = NULL;
1059                 break;
1060             }
1061             if (cur->next != NULL) {
1062                 cur = cur->next;
1063                 break;
1064             }
1065         } while (cur != NULL);
1066     }
1067     if (has_variables != 0) {
1068         xsltPopStack(ctxt);
1069     }
1070 }
1071
1072 /**
1073  * xsltIf:
1074  * @ctxt:  a XSLT process context
1075  * @node:  the node in the source tree.
1076  * @inst:  the xslt if node
1077  *
1078  * Process the xslt if node on the source node
1079  */
1080 void
1081 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
1082                    xmlNodePtr inst) {
1083     xmlChar *prop;
1084     xmlXPathObjectPtr res = NULL, tmp;
1085     xmlXPathParserContextPtr xpathParserCtxt = NULL;
1086     int doit = 1;
1087
1088     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1089         return;
1090
1091     prop = xmlGetNsProp(inst, (const xmlChar *)"test", XSLT_NAMESPACE);
1092     if (prop == NULL) {
1093         xsltGenericError(xsltGenericErrorContext,
1094              "xsltIf: test is not defined\n");
1095         return;
1096     }
1097 #ifdef DEBUG_PROCESS
1098     xsltGenericDebug(xsltGenericDebugContext,
1099          "xsltIf: test %s\n", prop);
1100 #endif
1101
1102     if (ctxt->xpathCtxt == NULL) {
1103         xmlXPathInit();
1104         ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
1105         if (ctxt->xpathCtxt == NULL)
1106             goto error;
1107         XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
1108     }
1109     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1110     if (xpathParserCtxt == NULL)
1111         goto error;
1112     ctxt->xpathCtxt->node = node;
1113     valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
1114     xmlXPathEvalExpr(xpathParserCtxt);
1115     xmlXPathBooleanFunction(xpathParserCtxt, 1);
1116     res = valuePop(xpathParserCtxt);
1117     do {
1118         tmp = valuePop(xpathParserCtxt);
1119         if (tmp != NULL) {
1120             xmlXPathFreeObject(tmp);
1121         }
1122     } while (tmp != NULL);
1123
1124     if (res != NULL) {
1125         if (res->type == XPATH_BOOLEAN)
1126             doit = res->boolval;
1127         else {
1128 #ifdef DEBUG_PROCESS
1129             xsltGenericDebug(xsltGenericDebugContext,
1130                 "xsltIf: test didn't evaluate to a boolean\n");
1131 #endif
1132             goto error;
1133         }
1134     }
1135
1136 #ifdef DEBUG_PROCESS
1137     xsltGenericDebug(xsltGenericDebugContext,
1138         "xsltIf: test evaluate to %d\n", doit);
1139 #endif
1140     if (doit) {
1141         xsltApplyOneTemplate(ctxt, ctxt->node, inst->children);
1142     }
1143
1144 error:
1145     if (xpathParserCtxt != NULL)
1146         xmlXPathFreeParserContext(xpathParserCtxt);
1147     if (prop != NULL)
1148         xmlFree(prop);
1149     if (res != NULL)
1150         xmlXPathFreeObject(res);
1151 }
1152
1153 /**
1154  * xsltForEach:
1155  * @ctxt:  a XSLT process context
1156  * @node:  the node in the source tree.
1157  * @inst:  the xslt for-each node
1158  *
1159  * Process the xslt for-each node on the source node
1160  */
1161 void
1162 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
1163                    xmlNodePtr inst) {
1164     xmlChar *prop;
1165     xmlXPathObjectPtr res = NULL, tmp;
1166     xmlNodePtr replacement;
1167     xmlNodeSetPtr list = NULL, oldlist;
1168     xmlXPathParserContextPtr xpathParserCtxt = NULL;
1169     int i, oldProximityPosition, oldContextSize;
1170
1171     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1172         return;
1173
1174     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
1175     if (prop == NULL) {
1176         xsltGenericError(xsltGenericErrorContext,
1177              "xsltForEach: select is not defined\n");
1178         return;
1179     }
1180 #ifdef DEBUG_PROCESS
1181     xsltGenericDebug(xsltGenericDebugContext,
1182          "xsltForEach: select %s\n", prop);
1183 #endif
1184
1185     if (ctxt->xpathCtxt == NULL) {
1186         xmlXPathInit();
1187         ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
1188         if (ctxt->xpathCtxt == NULL)
1189             goto error;
1190         XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
1191     }
1192     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1193     if (xpathParserCtxt == NULL)
1194         goto error;
1195     ctxt->xpathCtxt->node = node;
1196     valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
1197     xmlXPathEvalExpr(xpathParserCtxt);
1198     res = valuePop(xpathParserCtxt);
1199     do {
1200         tmp = valuePop(xpathParserCtxt);
1201         if (tmp != NULL) {
1202             xmlXPathFreeObject(tmp);
1203         }
1204     } while (tmp != NULL);
1205
1206     if (res != NULL) {
1207         if (res->type == XPATH_NODESET)
1208             list = res->nodesetval;
1209         else {
1210 #ifdef DEBUG_PROCESS
1211             xsltGenericDebug(xsltGenericDebugContext,
1212                 "xsltForEach: select didn't evaluate to a node list\n");
1213 #endif
1214             goto error;
1215         }
1216     }
1217
1218 #ifdef DEBUG_PROCESS
1219     xsltGenericDebug(xsltGenericDebugContext,
1220         "xsltForEach: select evaluate to %d nodes\n", list->nodeNr);
1221 #endif
1222
1223     oldlist = ctxt->nodeList;
1224     ctxt->nodeList = list;
1225     oldContextSize = ctxt->xpathCtxt->contextSize;
1226     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1227     ctxt->xpathCtxt->contextSize = list->nodeNr;
1228
1229     /* 
1230      * handle and skip the xsl:sort
1231      */
1232     replacement = inst->children;
1233     while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
1234         xsltSort(ctxt, node, replacement);
1235         replacement = replacement->next;
1236     }
1237
1238     for (i = 0;i < list->nodeNr;i++) {
1239         ctxt->node = list->nodeTab[i];
1240         ctxt->xpathCtxt->proximityPosition = i + 1;
1241         xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement);
1242     }
1243     ctxt->nodeList = oldlist;
1244     ctxt->xpathCtxt->contextSize = oldContextSize;
1245     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1246
1247 error:
1248     if (xpathParserCtxt != NULL)
1249         xmlXPathFreeParserContext(xpathParserCtxt);
1250     if (prop != NULL)
1251         xmlFree(prop);
1252     if (res != NULL)
1253         xmlXPathFreeObject(res);
1254 }
1255
1256 /**
1257  * xsltProcessOneNode:
1258  * @ctxt:  a XSLT process context
1259  * @node:  the node in the source tree.
1260  *
1261  * Process the source node.
1262  */
1263 void
1264 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1265     xsltTemplatePtr template;
1266
1267     template = xsltGetTemplate(ctxt->style, node);
1268     /*
1269      * If no template is found, apply the default rule.
1270      */
1271     if (template == NULL) {
1272 #ifdef DEBUG_PROCESS
1273         if (node->type == XML_DOCUMENT_NODE)
1274             xsltGenericDebug(xsltGenericDebugContext,
1275              "xsltProcessOneNode: no template found for /\n");
1276         else 
1277             xsltGenericDebug(xsltGenericDebugContext,
1278              "xsltProcessOneNode: no template found for %s\n", node->name);
1279 #endif
1280         xsltDefaultProcessOneNode(ctxt, node);
1281         return;
1282     }
1283
1284     xsltApplyOneTemplate(ctxt, node, template->content);
1285 }
1286
1287 /**
1288  * xsltApplyStylesheet:
1289  * @style:  a parsed XSLT stylesheet
1290  * @doc:  a parsed XML document
1291  *
1292  * Apply the stylesheet to the document
1293  * NOTE: This may lead to a non-wellformed output XML wise !
1294  *
1295  * Returns the result document or NULL in case of error
1296  */
1297 xmlDocPtr
1298 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc) {
1299     xmlDocPtr res = NULL;
1300     xsltTransformContextPtr ctxt = NULL;
1301     xmlNodePtr root;
1302
1303     if ((style == NULL) || (doc == NULL))
1304         return(NULL);
1305     ctxt = xsltNewTransformContext();
1306     if (ctxt == NULL)
1307         return(NULL);
1308     ctxt->doc = doc;
1309     ctxt->style = style;
1310     xsltEvalGlobalVariables(ctxt);
1311     if ((style->method != NULL) &&
1312         (!xmlStrEqual(style->method, (const xmlChar *) "xml"))) {
1313         if (xmlStrEqual(style->method, (const xmlChar *) "html")) {
1314             ctxt->type = XSLT_OUTPUT_HTML;
1315             res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
1316             if (res == NULL)
1317                 goto error;
1318         } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
1319             ctxt->type = XSLT_OUTPUT_TEXT;
1320             res = xmlNewDoc(style->version);
1321             if (res == NULL)
1322                 goto error;
1323         } else {
1324             xsltGenericError(xsltGenericErrorContext,
1325                              "xsltApplyStylesheet: insupported method %s\n",
1326                              style->method);
1327             goto error;
1328         }
1329     } else {
1330         ctxt->type = XSLT_OUTPUT_XML;
1331         res = xmlNewDoc(style->version);
1332         if (res == NULL)
1333             goto error;
1334     }
1335     res->charset = XML_CHAR_ENCODING_UTF8;
1336     if (style->encoding != NULL)
1337         res->encoding = xmlStrdup(style->encoding);
1338
1339     /*
1340      * Start.
1341      */
1342     ctxt->output = res;
1343     ctxt->insert = (xmlNodePtr) res;
1344     ctxt->node = (xmlNodePtr) doc;
1345     xsltProcessOneNode(ctxt, ctxt->node);
1346
1347
1348     if ((ctxt->type = XSLT_OUTPUT_XML) &&
1349         ((style->doctypePublic != NULL) ||
1350          (style->doctypeSystem != NULL))) {
1351         root = xmlDocGetRootElement(res);
1352         if (root != NULL)
1353             res->intSubset = xmlCreateIntSubset(res, root->name,
1354                          style->doctypePublic, style->doctypeSystem);
1355     }
1356     xmlXPathFreeNodeSet(ctxt->nodeList);
1357     xsltFreeTransformContext(ctxt);
1358     return(res);
1359
1360 error:
1361     if (res != NULL)
1362         xmlFreeDoc(res);
1363     if (ctxt != NULL)
1364         xsltFreeTransformContext(ctxt);
1365     return(NULL);
1366 }
1367