b41ea592e71fd261e3a3e4121fe9ea2ca1d2d1e8
[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  * References:
6  *   http://www.w3.org/TR/1999/REC-xslt-19991116
7  *
8  *   Michael Kay "XSLT Programmer's Reference" pp 637-643
9  *   Writing Multiple Output Files
10  *
11  *   XSLT-1.1 Working Draft
12  *   http://www.w3.org/TR/xslt11#multiple-output
13  *
14  * See Copyright for the status of this software.
15  *
16  * Daniel.Veillard@imag.fr
17  */
18
19 #include "xsltconfig.h"
20
21 #include <string.h>
22
23 #include <libxml/xmlmemory.h>
24 #include <libxml/parser.h>
25 #include <libxml/tree.h>
26 #include <libxml/valid.h>
27 #include <libxml/hash.h>
28 #include <libxml/encoding.h>
29 #include <libxml/xmlerror.h>
30 #include <libxml/xpath.h>
31 #include <libxml/parserInternals.h>
32 #include <libxml/xpathInternals.h>
33 #include <libxml/HTMLtree.h>
34 #include <libxml/uri.h>
35 #include "xslt.h"
36 #include "xsltInternals.h"
37 #include "xsltutils.h"
38 #include "pattern.h"
39 #include "transform.h"
40 #include "variables.h"
41 #include "numbersInternals.h"
42 #include "namespaces.h"
43 #include "attributes.h"
44 #include "templates.h"
45 #include "imports.h"
46 #include "keys.h"
47 #include "documents.h"
48 #include "extensions.h"
49 #include "extra.h"
50 #include "preproc.h"
51
52 #ifdef WITH_XSLT_DEBUG
53 #define WITH_XSLT_DEBUG_PROCESS
54 #endif
55
56 int xsltMaxDepth = 250;
57
58 /*
59  * Useful macros
60  */
61
62 #ifndef FALSE
63 # define FALSE (0 == 1)
64 # define TRUE (!FALSE)
65 #endif
66
67 #define IS_BLANK_NODE(n)                                                \
68     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
69
70 /*
71  * Generic function for accessing stacks in the transform Context
72  */
73
74 #define PUSH_AND_POP(scope, type, name)                                 \
75 scope int name##Push(xsltTransformContextPtr ctxt, type value) {        \
76     if (ctxt->name##Nr >= ctxt->name##Max) {                            \
77         ctxt->name##Max *= 2;                                           \
78         ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab,          \
79                      ctxt->name##Max * sizeof(ctxt->name##Tab[0]));     \
80         if (ctxt->name##Tab == NULL) {                                  \
81             xmlGenericError(xmlGenericErrorContext,                     \
82                     "realloc failed !\n");                              \
83             return(0);                                                  \
84         }                                                               \
85     }                                                                   \
86     ctxt->name##Tab[ctxt->name##Nr] = value;                            \
87     ctxt->name = value;                                                 \
88     return(ctxt->name##Nr++);                                           \
89 }                                                                       \
90 scope type name##Pop(xsltTransformContextPtr ctxt) {                    \
91     type ret;                                                           \
92     if (ctxt->name##Nr <= 0) return(0);                                 \
93     ctxt->name##Nr--;                                                   \
94     if (ctxt->name##Nr > 0)                                             \
95         ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1];               \
96     else                                                                \
97         ctxt->name = NULL;                                              \
98     ret = ctxt->name##Tab[ctxt->name##Nr];                              \
99     ctxt->name##Tab[ctxt->name##Nr] = 0;                                \
100     return(ret);                                                        \
101 }                                                                       \
102
103 /*
104  * Those macros actually generate the functions
105  */
106 PUSH_AND_POP(static, xsltTemplatePtr, templ)
107 PUSH_AND_POP(static, xsltStackElemPtr, vars)
108
109 /************************************************************************
110  *                                                                      *
111  *                      XInclude default settings                       *
112  *                                                                      *
113  ************************************************************************/
114
115 static int xsltDoXIncludeDefault = 0;
116
117 /**
118  * xsltSetXIncludeDefault:
119  * @xinclude: whether to do XInclude processing
120  *
121  * Set whether XInclude should be processed on document being loaded by default
122  */
123 void
124 xsltSetXIncludeDefault(int xinclude) {
125     xsltDoXIncludeDefault = (xinclude != 0);
126 }
127
128 /**
129  * xsltGetXIncludeDefault:
130  *
131  * return the default state for XInclude processing
132  *
133  * Returns 0 if there is no processing 1 otherwise
134  */
135 int
136 xsltGetXIncludeDefault(void) {
137     return(xsltDoXIncludeDefault);
138 }
139
140 /************************************************************************
141  *                                                                      *
142  *                      handling of transformation contexts             *
143  *                                                                      *
144  ************************************************************************/
145
146 /**
147  * xsltNewTransformContext:
148  * @style:  a parsed XSLT stylesheet
149  * @doc:  the input document
150  *
151  * Create a new XSLT TransformContext
152  *
153  * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
154  */
155 static xsltTransformContextPtr
156 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
157     xsltTransformContextPtr cur;
158     xsltDocumentPtr docu;
159
160     cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
161     if (cur == NULL) {
162         xsltGenericError(xsltGenericErrorContext,
163                 "xsltNewTransformContext : malloc failed\n");
164         return(NULL);
165     }
166     memset(cur, 0, sizeof(xsltTransformContext));
167
168     /*
169      * initialize the template stack
170      */
171     cur->templTab = (xsltTemplatePtr *)
172                 xmlMalloc(10 * sizeof(xsltTemplatePtr));
173     if (cur->templTab == NULL) {
174         xmlGenericError(xmlGenericErrorContext,
175                 "xsltNewTransformContext: out of memory\n");
176         xmlFree(cur);
177         return(NULL);
178     }
179     cur->templNr = 0;
180     cur->templMax = 5;
181     cur->templ = NULL;
182
183     /*
184      * initialize the variables stack
185      */
186     cur->varsTab = (xsltStackElemPtr *)
187                 xmlMalloc(10 * sizeof(xsltStackElemPtr));
188     if (cur->varsTab == NULL) {
189         xmlGenericError(xmlGenericErrorContext,
190                 "xsltNewTransformContext: out of memory\n");
191         xmlFree(cur->templTab);
192         xmlFree(cur);
193         return(NULL);
194     }
195     cur->varsNr = 0;
196     cur->varsMax = 5;
197     cur->vars = NULL;
198
199     cur->style = style;
200     xmlXPathInit();
201     cur->xpathCtxt = xmlXPathNewContext(doc);
202     if (cur->xpathCtxt == NULL) {
203         xsltGenericError(xsltGenericErrorContext,
204                 "xsltNewTransformContext : xmlXPathNewContext failed\n");
205         xmlFree(cur->templTab);
206         xmlFree(cur->varsTab);
207         xmlFree(cur);
208         return(NULL);
209     }
210     cur->xpathCtxt->proximityPosition = 0;
211     cur->xpathCtxt->contextSize = 0;
212     XSLT_REGISTER_VARIABLE_LOOKUP(cur);
213     cur->xpathCtxt->nsHash = style->nsHash;
214     docu = xsltNewDocument(cur, doc);
215     if (docu == NULL) {
216         xsltGenericError(xsltGenericErrorContext,
217                 "xsltNewTransformContext : xsltNewDocument failed\n");
218         xmlFree(cur->templTab);
219         xmlFree(cur->varsTab);
220         xmlFree(cur);
221         return(NULL);
222     }
223     docu->main = 1;
224     cur->document = docu;
225     cur->inst = NULL;
226     cur->xinclude = xsltDoXIncludeDefault;
227     return(cur);
228 }
229
230 /**
231  * xsltFreeTransformContext:
232  * @ctxt:  an XSLT parser context
233  *
234  * Free up the memory allocated by @ctxt
235  */
236 static void
237 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
238     if (ctxt == NULL)
239         return;
240     if (ctxt->xpathCtxt != NULL) {
241         ctxt->xpathCtxt->nsHash = NULL;
242         xmlXPathFreeContext(ctxt->xpathCtxt);
243     }
244     if (ctxt->templTab != NULL)
245         xmlFree(ctxt->templTab);
246     if (ctxt->varsTab != NULL)
247         xmlFree(ctxt->varsTab);
248     xsltFreeDocuments(ctxt);
249     xsltFreeCtxtExts(ctxt);
250     xsltFreeGlobalVariables(ctxt);
251     memset(ctxt, -1, sizeof(xsltTransformContext));
252     xmlFree(ctxt);
253 }
254
255 /************************************************************************
256  *                                                                      *
257  *                      Copy of Nodes in an XSLT fashion                *
258  *                                                                      *
259  ************************************************************************/
260
261 xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
262                         xmlNodePtr insert);
263
264 /**
265  * xsltCopyProp:
266  * @ctxt:  a XSLT process context
267  * @target:  the element where the attribute will be grafted
268  * @attr:  the attribute
269  *
270  * Do a copy of an attribute
271  *
272  * Returns: a new xmlAttrPtr, or NULL in case of error.
273  */
274 static xmlAttrPtr
275 xsltCopyProp(xsltTransformContextPtr ctxt, xmlNodePtr target,
276              xmlAttrPtr attr) {
277     xmlAttrPtr ret = NULL;
278     xmlNsPtr ns;
279     xmlChar *val;
280
281     if (attr == NULL)
282         return(NULL);
283
284     if (attr->ns != NULL) {
285         ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target);
286     } else {
287         ns = NULL;
288     }
289     val = xmlNodeListGetString(attr->doc, attr->children, 1);
290     ret = xmlSetNsProp(target, ns, attr->name, val);
291     if (val != NULL)
292         xmlFree(val);
293     return(ret);
294 }
295
296 /**
297  * xsltCopyPropList:
298  * @ctxt:  a XSLT process context
299  * @target:  the element where the attributes will be grafted
300  * @cur:  the first attribute
301  *
302  * Do a copy of an attribute list.
303  *
304  * Returns: a new xmlAttrPtr, or NULL in case of error.
305  */
306 static xmlAttrPtr
307 xsltCopyPropList(xsltTransformContextPtr ctxt, xmlNodePtr target,
308                  xmlAttrPtr cur) {
309     xmlAttrPtr ret = NULL;
310     xmlAttrPtr p = NULL,q;
311     xmlNsPtr ns;
312
313     while (cur != NULL) {
314         if (cur->ns != NULL) {
315             ns = xsltGetNamespace(ctxt, cur->parent, cur->ns, target);
316         } else {
317             ns = NULL;
318         }
319         q = xmlCopyProp(target, cur);
320         if (q != NULL) {
321             q->ns = ns;
322             if (p == NULL) {
323                 ret = p = q;
324             } else {
325                 p->next = q;
326                 q->prev = p;
327                 p = q;
328             }
329         }
330         cur = cur->next;
331     }
332     return(ret);
333 }
334
335 /**
336  * xsltCopyNode:
337  * @ctxt:  a XSLT process context
338  * @node:  the element node in the source tree.
339  * @insert:  the parent in the result tree.
340  *
341  * Make a copy of the element node @node
342  * and insert it as last child of @insert
343  *
344  * Returns a pointer to the new node, or NULL in case of error
345  */
346 static xmlNodePtr
347 xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
348              xmlNodePtr insert) {
349     xmlNodePtr copy;
350
351     copy = xmlCopyNode(node, 0);
352     if (copy != NULL) {
353         copy->doc = ctxt->output;
354         xmlAddChild(insert, copy);
355         if (node->type == XML_ELEMENT_NODE) {
356             /*
357              * Add namespaces as they are needed
358              */
359             if (node->nsDef != NULL)
360                 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
361         }
362         if (node->ns != NULL) {
363             copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
364         }
365     } else {
366         xsltGenericError(xsltGenericErrorContext,
367                 "xsltCopyNode: copy %s failed\n", node->name);
368     }
369     return(copy);
370 }
371
372 /**
373  * xsltCopyTreeList:
374  * @ctxt:  a XSLT process context
375  * @list:  the list of element node in the source tree.
376  * @insert:  the parent in the result tree.
377  *
378  * Make a copy of the full list of tree @list
379  * and insert them as last children of @insert
380  *
381  * Returns a pointer to the new list, or NULL in case of error
382  */
383 static xmlNodePtr
384 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr list,
385              xmlNodePtr insert) {
386     xmlNodePtr copy, ret = NULL;
387
388     while (list != NULL) {
389         copy = xsltCopyTree(ctxt, list, insert);
390         if (copy != NULL) {
391             if (ret == NULL) {
392                 ret = copy;
393             }
394         }
395         list = list->next;
396     }
397     return(ret);
398 }
399
400 /**
401  * xsltCopyTree:
402  * @ctxt:  a XSLT process context
403  * @node:  the element node in the source tree.
404  * @insert:  the parent in the result tree.
405  *
406  * Make a copy of the full tree under the element node @node
407  * and insert it as last child of @insert
408  *
409  * Returns a pointer to the new tree, or NULL in case of error
410  */
411 xmlNodePtr
412 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
413              xmlNodePtr insert) {
414     xmlNodePtr copy;
415
416     if (node == NULL)
417         return(NULL);
418     switch (node->type) {
419         case XML_ELEMENT_NODE:
420         case XML_TEXT_NODE:
421         case XML_CDATA_SECTION_NODE:
422         case XML_ENTITY_REF_NODE:
423         case XML_ENTITY_NODE:
424         case XML_PI_NODE:
425         case XML_COMMENT_NODE:
426         case XML_DOCUMENT_NODE:
427         case XML_HTML_DOCUMENT_NODE:
428 #ifdef LIBXML_DOCB_ENABLED
429         case XML_DOCB_DOCUMENT_NODE:
430 #endif
431             break;
432         case XML_ATTRIBUTE_NODE:
433             return((xmlNodePtr)
434                    xsltCopyProp(ctxt, insert, (xmlAttrPtr) node));
435         case XML_NAMESPACE_DECL:
436             return((xmlNodePtr)
437                    xsltCopyNamespaceList(ctxt, insert, (xmlNsPtr) node));
438             
439         case XML_DOCUMENT_TYPE_NODE:
440         case XML_DOCUMENT_FRAG_NODE:
441         case XML_NOTATION_NODE:
442         case XML_DTD_NODE:
443         case XML_ELEMENT_DECL:
444         case XML_ATTRIBUTE_DECL:
445         case XML_ENTITY_DECL:
446         case XML_XINCLUDE_START:
447         case XML_XINCLUDE_END:
448             return(NULL);
449     }
450     copy = xmlCopyNode(node, 0);
451     copy->doc = ctxt->output;
452     if (copy != NULL) {
453         xmlAddChild(insert, copy);
454         copy->next = NULL;
455         /*
456          * Add namespaces as they are needed
457          */
458         if (node->nsDef != NULL)
459             xsltCopyNamespaceList(ctxt, copy, node->nsDef);
460         if (node->ns != NULL) {
461             copy->ns = xsltGetNamespace(ctxt, node, node->ns, insert);
462         }
463         if (node->properties != NULL)
464             copy->properties = xsltCopyPropList(ctxt, copy,
465                                                node->properties);
466         if (node->children != NULL)
467             xsltCopyTreeList(ctxt, node->children, copy);
468     } else {
469         xsltGenericError(xsltGenericErrorContext,
470                 "xsltCopyTree: copy %s failed\n", node->name);
471     }
472     return(copy);
473 }
474
475 /************************************************************************
476  *                                                                      *
477  *                      Default processing                              *
478  *                                                                      *
479  ************************************************************************/
480
481 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
482 /**
483  * xsltDefaultProcessOneNode:
484  * @ctxt:  a XSLT process context
485  * @node:  the node in the source tree.
486  *
487  * Process the source node with the default built-in template rule:
488  * <xsl:template match="*|/">
489  *   <xsl:apply-templates/>
490  * </xsl:template>
491  *
492  * and
493  *
494  * <xsl:template match="text()|@*">
495  *   <xsl:value-of select="."/>
496  * </xsl:template>
497  *
498  * Note also that namespaces declarations are copied directly:
499  *
500  * the built-in template rule is the only template rule that is applied
501  * for namespace nodes.
502  */
503 static void
504 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
505     xmlNodePtr copy;
506     xmlAttrPtr attrs;
507     xmlNodePtr delete = NULL, cur;
508     int strip_spaces = -1;
509     int nbchild = 0, oldSize;
510     int childno = 0, oldPos;
511     xsltTemplatePtr template;
512
513     CHECK_STOPPED;
514     /*
515      * Handling of leaves
516      */
517     switch (node->type) {
518         case XML_DOCUMENT_NODE:
519         case XML_HTML_DOCUMENT_NODE:
520         case XML_ELEMENT_NODE:
521             break;
522         case XML_CDATA_SECTION_NODE:
523             template = xsltGetTemplate(ctxt, node, NULL);
524             if (template) {
525                 xmlNodePtr oldNode;
526
527 #ifdef WITH_XSLT_DEBUG_PROCESS
528                 xsltGenericDebug(xsltGenericDebugContext,
529                  "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
530                                  node->content);
531 #endif
532                 oldNode = ctxt->node;
533                 ctxt->node = node;
534                 templPush(ctxt, template);
535                 xsltApplyOneTemplate(ctxt, node, template->content, 1);
536                 templPop(ctxt);
537                 ctxt->node = oldNode;
538             } else /* if (ctxt->mode == NULL) */ {
539 #ifdef WITH_XSLT_DEBUG_PROCESS
540                 xsltGenericDebug(xsltGenericDebugContext,
541                  "xsltDefaultProcessOneNode: copy CDATA %s\n",
542                                  node->content);
543 #endif
544                 copy = xmlNewDocText(ctxt->output, node->content);
545                 if (copy != NULL) {
546                     xmlAddChild(ctxt->insert, copy);
547                 } else {
548                     xsltGenericError(xsltGenericErrorContext,
549                         "xsltDefaultProcessOneNode: cdata copy failed\n");
550                 }
551             }
552             return;
553         case XML_TEXT_NODE:
554             template = xsltGetTemplate(ctxt, node, NULL);
555             if (template) {
556                 xmlNodePtr oldNode;
557
558 #ifdef WITH_XSLT_DEBUG_PROCESS
559                 xsltGenericDebug(xsltGenericDebugContext,
560                  "xsltDefaultProcessOneNode: applying template for text %s\n",
561                                  node->content);
562 #endif
563                 oldNode = ctxt->node;
564                 ctxt->node = node;
565                 templPush(ctxt, template);
566                 xsltApplyOneTemplate(ctxt, node, template->content, 1);
567                 templPop(ctxt);
568                 ctxt->node = oldNode;
569             } else /* if (ctxt->mode == NULL) */ {
570 #ifdef WITH_XSLT_DEBUG_PROCESS
571                 if (node->content == NULL)
572                     xsltGenericDebug(xsltGenericDebugContext,
573                      "xsltDefaultProcessOneNode: copy empty text\n");
574                 else
575                     xsltGenericDebug(xsltGenericDebugContext,
576                      "xsltDefaultProcessOneNode: copy text %s\n",
577                                      node->content);
578 #endif
579                 copy = xmlCopyNode(node, 0);
580                 if (copy != NULL) {
581                     xmlAddChild(ctxt->insert, copy);
582                 } else {
583                     xsltGenericError(xsltGenericErrorContext,
584                         "xsltDefaultProcessOneNode: text copy failed\n");
585                 }
586             }
587             return;
588         case XML_ATTRIBUTE_NODE:
589             if (ctxt->insert->type == XML_ELEMENT_NODE) {
590                     xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, current;
591                 template = xsltGetTemplate(ctxt, node, NULL);
592                 if (template) {
593                     xmlNodePtr oldNode;
594
595                     oldNode = ctxt->node;
596                     ctxt->node = node;
597                     templPush(ctxt, template);
598                     xsltApplyOneTemplate(ctxt, node, template->content, 1);
599                     templPop(ctxt);
600                     ctxt->node = oldNode;
601                 } else if (ctxt->mode == NULL) {
602                     if (attr->ns != NULL) {
603                         if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
604                             (xmlStrncasecmp(attr->ns->prefix,
605                                             (xmlChar *)"xml", 3))) {
606                             ret = xmlCopyProp(ctxt->insert, attr);
607                             ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
608                                                        ctxt->insert);
609                         } 
610                     } else
611                         ret = xmlCopyProp(ctxt->insert, attr);
612
613                     current = ctxt->insert->properties;
614                     if (current != NULL) {
615                         if ((xmlStrEqual(current->name, ret->name)) &&
616                             (current->ns == ret->ns)) {
617                             xmlNodePtr tmp;
618                             tmp = current->children;
619                             current->children = ret->children;
620                             ret->children = tmp;
621                             xmlFreeProp(ret);
622                             return;
623                         }
624                         while (current->next != NULL) {
625                             current = current->next;
626                             if ((xmlStrEqual(current->name, ret->name)) &&
627                                 (current->ns == ret->ns)) {
628                                 xmlNodePtr tmp;
629                                 tmp = current->children;
630                                 current->children = ret->children;
631                                 ret->children = tmp;
632                                 xmlFreeProp(ret);
633                                 return;
634                             }
635                         }
636                         current->next = ret;
637                         ret->prev = current;
638                     } else
639                         ctxt->insert->properties = ret;
640                 }
641             }
642             return;
643         default:
644             return;
645     }
646     /*
647      * Handling of Elements: first pass, cleanup and counting
648      */
649     cur = node->children;
650     while (cur != NULL) {
651         switch (cur->type) {
652             case XML_TEXT_NODE:
653                 if ((IS_BLANK_NODE(cur)) &&
654                     (cur->parent != NULL) &&
655                     (ctxt->style->stripSpaces != NULL)) {
656                     if (strip_spaces == -1)
657                         strip_spaces =
658                             xsltFindElemSpaceHandling(ctxt, cur->parent);
659                     if (strip_spaces == 1) {
660                         delete = cur;
661                         break;
662                     }
663                 }
664                 /* no break on purpose */
665             case XML_CDATA_SECTION_NODE:
666             case XML_DOCUMENT_NODE:
667             case XML_HTML_DOCUMENT_NODE:
668             case XML_ELEMENT_NODE:
669             case XML_PI_NODE:
670             case XML_COMMENT_NODE:
671                 nbchild++;
672                 break;
673             default:
674 #ifdef WITH_XSLT_DEBUG_PROCESS
675                 xsltGenericDebug(xsltGenericDebugContext,
676                  "xsltDefaultProcessOneNode: skipping node type %d\n",
677                                  cur->type);
678 #endif
679                 delete = cur;
680         }
681         cur = cur->next;
682         if (delete != NULL) {
683 #ifdef WITH_XSLT_DEBUG_PROCESS
684             xsltGenericDebug(xsltGenericDebugContext,
685                  "xsltDefaultProcessOneNode: removing ignorable blank node\n");
686 #endif
687             xmlUnlinkNode(delete);
688             xmlFreeNode(delete);
689             delete = NULL;
690         }
691     }
692     /*
693      * Handling of Elements: second pass, actual processing
694      */
695     attrs = node->properties;
696     while (attrs != NULL) {
697         template = xsltGetTemplate(ctxt, (xmlNodePtr) attrs, NULL);
698         if (template) {
699             xmlNodePtr oldNode;
700
701             oldNode = ctxt->node;
702             ctxt->node = node;
703             templPush(ctxt, template);
704             xsltApplyOneTemplate(ctxt, node, template->content, 1);
705             templPop(ctxt);
706             ctxt->node = oldNode;
707         }
708         attrs = attrs->next;
709     }
710     oldSize = ctxt->xpathCtxt->contextSize;
711     oldPos = ctxt->xpathCtxt->proximityPosition;
712     cur = node->children;
713     while (cur != NULL) {
714         childno++;
715         switch (cur->type) {
716             case XML_DOCUMENT_NODE:
717             case XML_HTML_DOCUMENT_NODE:
718             case XML_ELEMENT_NODE:
719                 ctxt->xpathCtxt->contextSize = nbchild;
720                 ctxt->xpathCtxt->proximityPosition = childno;
721                 varsPush( ctxt, NULL );
722                 xsltProcessOneNode(ctxt, cur);
723                 xsltFreeStackElemList( varsPop(ctxt) );
724                 break;
725             case XML_CDATA_SECTION_NODE:
726                 template = xsltGetTemplate(ctxt, node, NULL);
727                 if (template) {
728                     xmlNodePtr oldNode;
729
730 #ifdef WITH_XSLT_DEBUG_PROCESS
731                     xsltGenericDebug(xsltGenericDebugContext,
732                  "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
733                                      node->content);
734 #endif
735                     oldNode = ctxt->node;
736                     ctxt->node = node;
737                     templPush(ctxt, template);
738                     xsltApplyOneTemplate(ctxt, node, template->content, 1);
739                     templPop(ctxt);
740                     ctxt->node = oldNode;
741                 } else /* if (ctxt->mode == NULL) */ {
742 #ifdef WITH_XSLT_DEBUG_PROCESS
743                     xsltGenericDebug(xsltGenericDebugContext,
744                      "xsltDefaultProcessOneNode: copy CDATA %s\n",
745                                      node->content);
746 #endif
747                     copy = xmlNewDocText(ctxt->output, node->content);
748                     if (copy != NULL) {
749                         xmlAddChild(ctxt->insert, copy);
750                     } else {
751                         xsltGenericError(xsltGenericErrorContext,
752                             "xsltDefaultProcessOneNode: cdata copy failed\n");
753                     }
754                 }
755                 break;
756             case XML_TEXT_NODE:
757                 template = xsltGetTemplate(ctxt, cur, NULL);
758                 if (template) {
759                     xmlNodePtr oldNode;
760
761 #ifdef WITH_XSLT_DEBUG_PROCESS
762                     xsltGenericDebug(xsltGenericDebugContext,
763              "xsltDefaultProcessOneNode: applying template for text %s\n",
764                                      node->content);
765 #endif
766                     oldNode = ctxt->node;
767                     ctxt->node = cur;
768                     ctxt->xpathCtxt->contextSize = nbchild;
769                     ctxt->xpathCtxt->proximityPosition = childno;
770                     templPush(ctxt, template);
771                     xsltApplyOneTemplate(ctxt, cur, template->content, 1);
772                     templPop(ctxt);
773                     ctxt->node = oldNode;
774                 } else /* if (ctxt->mode == NULL) */ {
775 #ifdef WITH_XSLT_DEBUG_PROCESS
776                     if (cur->content == NULL)
777                         xsltGenericDebug(xsltGenericDebugContext,
778                          "xsltDefaultProcessOneNode: copy empty text\n");
779                     else
780                         xsltGenericDebug(xsltGenericDebugContext,
781                      "xsltDefaultProcessOneNode: copy text %s\n",
782                                          cur->content);
783 #endif
784                     copy = xmlCopyNode(cur, 0);
785                     if (copy != NULL) {
786                         xmlAddChild(ctxt->insert, copy);
787                     } else {
788                         xsltGenericError(xsltGenericErrorContext,
789                             "xsltDefaultProcessOneNode: text copy failed\n");
790                     }
791                 }
792                 break;
793             case XML_PI_NODE:
794             case XML_COMMENT_NODE:
795                 template = xsltGetTemplate(ctxt, cur, NULL);
796                 if (template) {
797                     xmlNodePtr oldNode;
798
799 #ifdef WITH_XSLT_DEBUG_PROCESS
800                     if (cur->type == XML_PI_NODE)
801                         xsltGenericDebug(xsltGenericDebugContext,
802                      "xsltDefaultProcessOneNode: template found for PI %s\n",
803                                          cur->name);
804                     else if (cur->type == XML_COMMENT_NODE)
805                         xsltGenericDebug(xsltGenericDebugContext,
806                      "xsltDefaultProcessOneNode: template found for comment\n");
807 #endif
808                     oldNode = ctxt->node;
809                     ctxt->node = cur;
810                     ctxt->xpathCtxt->contextSize = nbchild;
811                     ctxt->xpathCtxt->proximityPosition = childno;
812                     templPush(ctxt, template);
813                     xsltApplyOneTemplate(ctxt, cur, template->content, 1);
814                     templPop(ctxt);
815                     ctxt->node = oldNode;
816                 }
817                 break;
818             default:
819                 break;
820         }
821         cur = cur->next;
822     }
823     ctxt->xpathCtxt->contextSize = oldSize;
824     ctxt->xpathCtxt->proximityPosition = oldPos;
825 }
826
827 /**
828  * xsltProcessOneNode:
829  * @ctxt:  a XSLT process context
830  * @node:  the node in the source tree.
831  *
832  * Process the source node.
833  */
834 void
835 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
836     xsltTemplatePtr template;
837     xmlNodePtr oldNode;
838
839     /*
840      * Cleanup children empty nodes if asked for
841      */
842     if ((node->children != NULL) &&
843         (xsltFindElemSpaceHandling(ctxt, node))) {
844         xmlNodePtr delete = NULL, cur = node->children;
845
846         while (cur != NULL) {
847             if (IS_BLANK_NODE(cur))
848                 delete = cur;
849             
850             cur = cur->next;
851             if (delete != NULL) {
852 #ifdef WITH_XSLT_DEBUG_PROCESS
853                 xsltGenericDebug(xsltGenericDebugContext,
854              "xsltProcessOneNode: removing ignorable blank node\n");
855 #endif
856                 xmlUnlinkNode(delete);
857                 xmlFreeNode(delete);
858                 delete = NULL;
859             }
860         }
861     }
862
863     template = xsltGetTemplate(ctxt, node, NULL);
864     /*
865      * If no template is found, apply the default rule.
866      */
867     if (template == NULL) {
868 #ifdef WITH_XSLT_DEBUG_PROCESS
869         if (node->type == XML_DOCUMENT_NODE)
870             xsltGenericDebug(xsltGenericDebugContext,
871              "xsltProcessOneNode: no template found for /\n");
872         else if (node->type == XML_CDATA_SECTION_NODE)
873             xsltGenericDebug(xsltGenericDebugContext,
874              "xsltProcessOneNode: no template found for CDATA\n");
875         else if (node->type == XML_ATTRIBUTE_NODE)
876             xsltGenericDebug(xsltGenericDebugContext,
877              "xsltProcessOneNode: no template found for attribute %s\n",
878                              ((xmlAttrPtr) node)->name);
879         else 
880             xsltGenericDebug(xsltGenericDebugContext,
881              "xsltProcessOneNode: no template found for %s\n", node->name);
882 #endif
883         oldNode = ctxt->node;
884         ctxt->node = node;
885         xsltDefaultProcessOneNode(ctxt, node);
886         ctxt->node = oldNode;
887         return;
888     }
889
890     if (node->type == XML_ATTRIBUTE_NODE) {
891 #ifdef WITH_XSLT_DEBUG_PROCESS
892         xsltGenericDebug(xsltGenericDebugContext,
893              "xsltProcessOneNode: applying template '%s' for attribute %s\n",
894                          template->match, node->name);
895 #endif
896         templPush(ctxt, template);
897         xsltApplyOneTemplate(ctxt, node, template->content, 1);
898         templPop(ctxt);
899     } else {
900 #ifdef WITH_XSLT_DEBUG_PROCESS
901         if (node->type == XML_DOCUMENT_NODE)
902             xsltGenericDebug(xsltGenericDebugContext,
903              "xsltProcessOneNode: applying template '%s' for /\n",
904                              template->match);
905         else 
906             xsltGenericDebug(xsltGenericDebugContext,
907              "xsltProcessOneNode: applying template '%s' for %s\n",
908                              template->match, node->name);
909 #endif
910         oldNode = ctxt->node;
911         ctxt->node = node;
912         templPush(ctxt, template);
913         xsltApplyOneTemplate(ctxt, node, template->content, 1);
914         templPop(ctxt);
915         ctxt->node = oldNode;
916     }
917 }
918
919 /**
920  * xsltApplyOneTemplate:
921  * @ctxt:  a XSLT process context
922  * @node:  the node in the source tree.
923  * @list:  the template replacement nodelist
924  * @real: is this a real template processing
925  *
926  * Process the apply-templates node on the source node
927  */
928 void
929 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
930                      xmlNodePtr list, int real) {
931     xmlNodePtr cur = NULL, insert, copy = NULL;
932     xmlNodePtr oldInsert;
933     xmlNodePtr oldCurrent = NULL;
934     xmlNodePtr oldInst = NULL;
935     xmlAttrPtr attrs;
936
937     if (list == NULL)
938         return;
939     CHECK_STOPPED;
940
941     if (ctxt->templNr >= xsltMaxDepth) {
942         xsltGenericError(xsltGenericErrorContext,
943                 "xsltApplyOneTemplate: loop found ???\n");
944         xsltGenericError(xsltGenericErrorContext,
945                 "try increasing xsltMaxDepth (--maxdepth)\n");
946         xsltDebug(ctxt, node, list, NULL);
947         return;
948     }
949
950     /*
951      * stack and saves
952      */
953     oldInsert = insert = ctxt->insert;
954     oldInst = ctxt->inst;
955     if (real) {
956         oldCurrent = ctxt->node;
957         ctxt->node = node;
958     }
959
960     /*
961      * Insert all non-XSLT nodes found in the template
962      */
963     cur = list;
964     while (cur != NULL) {
965         ctxt->inst = cur;
966         /*
967          * test, we must have a valid insertion point
968          */
969         if (insert == NULL) {
970 #ifdef WITH_XSLT_DEBUG_PROCESS
971             xsltGenericDebug(xsltGenericDebugContext,
972                  "xsltApplyOneTemplate: insert == NULL !\n");
973 #endif
974             if (real)
975                 ctxt->node = oldCurrent;
976             ctxt->inst = oldInst;
977             return;
978         }
979
980         if (IS_XSLT_ELEM(cur)) {
981             /*
982              * This is an XSLT node
983              */
984             xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->_private;
985             if (info == NULL) {
986                 if (IS_XSLT_NAME(cur, "message")) {
987                     xsltMessage(ctxt, node, cur);
988                 } else {
989                     xsltGenericError(xsltGenericDebugContext,
990                      "xsltApplyOneTemplate: %s was not compiled\n",
991                                      cur->name);
992                 }
993                 goto skip_children;
994             }
995             
996             if (info->func != NULL) {
997                 ctxt->insert = insert;
998                 info->func(ctxt, node, cur, info);
999                 ctxt->insert = oldInsert;
1000                 goto skip_children;
1001             }
1002
1003             if (IS_XSLT_NAME(cur, "variable")) {
1004                 xsltParseStylesheetVariable(ctxt, cur);
1005             } else if (IS_XSLT_NAME(cur, "param")) {
1006                 xsltParseStylesheetParam(ctxt, cur);
1007             } else if (IS_XSLT_NAME(cur, "message")) {
1008                 xsltMessage(ctxt, node, cur);
1009             } else {
1010                 xsltGenericError(xsltGenericDebugContext,
1011                      "xsltApplyOneTemplate: problem with xsl:%s\n",
1012                                  cur->name);
1013             }
1014             CHECK_STOPPED;
1015             goto skip_children;
1016         } else if ((cur->type == XML_TEXT_NODE) ||
1017                    (cur->type == XML_CDATA_SECTION_NODE)) {
1018             /*
1019              * This text comes from the stylesheet
1020              * For stylesheets, the set of whitespace-preserving
1021              * element names consists of just xsl:text.
1022              */
1023 #ifdef WITH_XSLT_DEBUG_PROCESS
1024             if (cur->type == XML_CDATA_SECTION_NODE)
1025                 xsltGenericDebug(xsltGenericDebugContext,
1026                      "xsltApplyOneTemplate: copy CDATA text %s\n",
1027                                  cur->content);
1028             else if (cur->name == xmlStringTextNoenc)
1029                 xsltGenericDebug(xsltGenericDebugContext,
1030                      "xsltApplyOneTemplate: copy unescaped text %s\n",
1031                                  cur->content);
1032             else
1033                 xsltGenericDebug(xsltGenericDebugContext,
1034                      "xsltApplyOneTemplate: copy text %s\n", cur->content);
1035 #endif
1036             copy = xmlNewText(cur->content);
1037             if (copy != NULL) {
1038                 if ((cur->name == xmlStringTextNoenc) ||
1039                     (cur->type == XML_CDATA_SECTION_NODE))
1040                     copy->name = xmlStringTextNoenc;
1041                 xmlAddChild(insert, copy);
1042             } else {
1043                 xsltGenericError(xsltGenericErrorContext,
1044                         "xsltApplyOneTemplate: text copy failed\n");
1045             }
1046         } else if ((cur->type == XML_ELEMENT_NODE) && 
1047                    (cur->ns != NULL) && (cur->_private != NULL)) {
1048             xsltTransformFunction function;
1049             /*
1050              * Flagged as an extension element
1051              */
1052             function = (xsltTransformFunction)
1053                 xmlHashLookup2(ctxt->extElements, cur->name, cur->ns->href);
1054             if (function == NULL) {
1055                 xsltGenericError(xsltGenericErrorContext,
1056                         "xsltApplyOneTemplate: failed to find extension %s\n",
1057                                  cur->name);
1058             } else {
1059 #ifdef WITH_XSLT_DEBUG_PROCESS
1060                 xsltGenericDebug(xsltGenericDebugContext,
1061                  "xsltApplyOneTemplate: extension construct %s\n", cur->name);
1062 #endif
1063
1064                 ctxt->insert = insert;
1065                 function(ctxt, node, cur, cur->_private);
1066                 ctxt->insert = oldInsert;
1067             }
1068             goto skip_children;
1069         } else if (cur->type == XML_ELEMENT_NODE) {
1070 #ifdef WITH_XSLT_DEBUG_PROCESS
1071             xsltGenericDebug(xsltGenericDebugContext,
1072                  "xsltApplyOneTemplate: copy node %s\n", cur->name);
1073 #endif
1074             copy = xsltCopyNode(ctxt, cur, insert);
1075             /*
1076              * all the attributes are directly inherited
1077              */
1078             if (cur->properties != NULL) {
1079                 attrs = xsltAttrListTemplateProcess(ctxt, copy,
1080                                                     cur->properties);
1081             }
1082         }
1083
1084         /*
1085          * Skip to next node, in document order.
1086          */
1087         if (cur->children != NULL) {
1088             if (cur->children->type != XML_ENTITY_DECL) {
1089                 cur = cur->children;
1090                 if (copy != NULL)
1091                     insert = copy;
1092                 continue;
1093             }
1094         }
1095 skip_children:
1096         if (cur->next != NULL) {
1097             cur = cur->next;
1098             continue;
1099         }
1100         
1101         do {
1102             cur = cur->parent;
1103             insert = insert->parent;
1104             if (cur == NULL)
1105                 break;
1106             if (cur == list->parent) {
1107                 cur = NULL;
1108                 break;
1109             }
1110             if (cur->next != NULL) {
1111                 cur = cur->next;
1112                 break;
1113             }
1114         } while (cur != NULL);
1115     }
1116     if (real)
1117         ctxt->node = oldCurrent;
1118     ctxt->inst = oldInst;
1119 }
1120
1121
1122 /************************************************************************
1123  *                                                                      *
1124  *                  XSLT-1.1 extensions                                 *
1125  *                                                                      *
1126  ************************************************************************/
1127
1128 /**
1129  * xsltDocumentElem:
1130  * @ctxt:  an XSLT processing context
1131  * @node:  The current node
1132  * @inst:  the instruction in the stylesheet
1133  * @comp:  precomputed informations
1134  *
1135  * Process an XSLT-1.1 document element
1136  */
1137 void 
1138 xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
1139                  xmlNodePtr inst, xsltStylePreCompPtr comp) {
1140     xsltStylesheetPtr style = NULL;
1141     int ret;
1142     xmlChar *filename = NULL;
1143     xmlDocPtr result = NULL;
1144     xmlDocPtr oldOutput;
1145     xmlNodePtr oldInsert;
1146
1147     if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
1148         return;
1149
1150     if (comp->filename == NULL) {
1151         xmlChar *base = NULL;
1152         xmlChar *URL = NULL;
1153         if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
1154 #ifdef WITH_XSLT_DEBUG_EXTRA
1155             xsltGenericDebug(xsltGenericDebugContext,
1156                 "Found saxon:output extension\n");
1157 #endif
1158             filename = xsltEvalAttrValueTemplate(ctxt, inst,
1159                              (const xmlChar *)"file",
1160                              XSLT_SAXON_NAMESPACE);
1161         } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
1162 #ifdef WITH_XSLT_DEBUG_EXTRA
1163             xsltGenericDebug(xsltGenericDebugContext,
1164                 "Found xalan:write extension\n");
1165 #endif
1166             filename = xsltEvalAttrValueTemplate(ctxt, inst,
1167                              (const xmlChar *)"select",
1168                              XSLT_XALAN_NAMESPACE);
1169         } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
1170             filename = xsltEvalAttrValueTemplate(ctxt, inst,
1171                              (const xmlChar *)"href",
1172                              XSLT_XT_NAMESPACE);
1173             if (filename == NULL) {
1174 #ifdef WITH_XSLT_DEBUG_EXTRA
1175                 xsltGenericDebug(xsltGenericDebugContext,
1176                     "Found xslt11:document construct\n");
1177 #endif
1178                 filename = xsltEvalAttrValueTemplate(ctxt, inst,
1179                                  (const xmlChar *)"href",
1180                                  XSLT_NAMESPACE);
1181                 comp->ver11 = 1;
1182             } else {
1183 #ifdef WITH_XSLT_DEBUG_EXTRA
1184                 xsltGenericDebug(xsltGenericDebugContext,
1185                     "Found xt:document extension\n");
1186 #endif
1187                 comp->ver11 = 0;
1188             }
1189         }
1190         if (filename == NULL)
1191             return;
1192
1193         /*
1194          * Compute output URL
1195          */
1196         base = xmlNodeGetBase(inst->doc, inst);
1197         URL = xmlBuildURI(filename, base);
1198         if (URL == NULL) {
1199             xsltGenericError(xsltGenericErrorContext,
1200                 "xsltDocumentElem: URL computation failed %s\n", filename);
1201         } else {
1202             xmlFree(filename);
1203             filename = URL;
1204         }
1205         if (base != NULL)
1206             xmlFree(base);
1207     } else {
1208         filename = xmlStrdup(comp->filename);
1209     }
1210
1211     oldOutput = ctxt->output;
1212     oldInsert = ctxt->insert;
1213
1214     style = xsltNewStylesheet();
1215     if (style == NULL) {
1216         xsltGenericError(xsltGenericErrorContext,
1217             "xsltDocumentElem: out of memory\n");
1218         goto error;
1219     }
1220
1221     /*
1222      * Version described in 1.1 draft allows full parametrization
1223      * of the output.
1224      */
1225     xsltParseStylesheetOutput(style, inst);
1226
1227     /*
1228      * Create a new document tree and process the element template
1229      */
1230     result = xmlNewDoc(style->version);
1231     if (result == NULL) {
1232         xsltGenericError(xsltGenericErrorContext,
1233             "xsltDocumentElem: out of memory\n");
1234         goto error;
1235     }
1236     ctxt->output = result;
1237     ctxt->insert = (xmlNodePtr) result;
1238     varsPush(ctxt, NULL);
1239     xsltApplyOneTemplate(ctxt, node, inst->children, 0);
1240     xsltFreeStackElemList(varsPop(ctxt));
1241
1242     /*
1243      * Save the result
1244      */
1245     ret = xsltSaveResultToFilename((const char *) filename,
1246                                    result, style, 0);
1247     if (ret < 0) {
1248         xsltGenericError(xsltGenericErrorContext,
1249             "xsltDocumentElem: unable to save to %s\n", filename);
1250 #ifdef WITH_XSLT_DEBUG_EXTRA
1251     } else {
1252         xsltGenericDebug(xsltGenericDebugContext,
1253             "Wrote %d bytes to %s\n", ret, , filename);
1254 #endif
1255     }
1256
1257 error:
1258     ctxt->output = oldOutput;
1259     ctxt->insert = oldInsert;
1260     if (filename != NULL)
1261         xmlFree(filename);
1262     if (style != NULL)
1263         xsltFreeStylesheet(style);
1264     if (result != NULL)
1265         xmlFreeDoc(result);
1266 }
1267
1268 /************************************************************************
1269  *                                                                      *
1270  *              Most of the XSLT-1.0 transformations                    *
1271  *                                                                      *
1272  ************************************************************************/
1273
1274 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
1275
1276 /**
1277  * xsltSort:
1278  * @ctxt:  a XSLT process context
1279  * @node:  the node in the source tree.
1280  * @inst:  the xslt sort node
1281  * @comp:  precomputed informations
1282  *
1283  * function attached to xsl:sort nodes, but this should not be
1284  * called directly
1285  */
1286 void
1287 xsltSort(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
1288         xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst ATTRIBUTE_UNUSED,
1289         xsltStylePreCompPtr comp) {
1290     if (comp == NULL) {
1291         xsltGenericError(xsltGenericErrorContext,
1292              "xsl:sort : compilation failed\n");
1293         return;
1294     }
1295     xsltGenericError(xsltGenericErrorContext,
1296          "xsl:sort : improper use this should not be reached\n");
1297 }
1298
1299 /**
1300  * xsltCopy:
1301  * @ctxt:  a XSLT process context
1302  * @node:  the node in the source tree.
1303  * @inst:  the xslt copy node
1304  * @comp:  precomputed informations
1305  *
1306  * Process the xslt copy node on the source node
1307  */
1308 void
1309 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
1310                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
1311     xmlNodePtr copy, oldInsert;
1312
1313     oldInsert = ctxt->insert;
1314     if (ctxt->insert != NULL) {
1315         switch (node->type) {
1316             case XML_TEXT_NODE:
1317             case XML_CDATA_SECTION_NODE:
1318                 /*
1319                  * This text comes from the stylesheet
1320                  * For stylesheets, the set of whitespace-preserving
1321                  * element names consists of just xsl:text.
1322                  */
1323 #ifdef WITH_XSLT_DEBUG_PROCESS
1324                 if (node->type == XML_CDATA_SECTION_NODE)
1325                     xsltGenericDebug(xsltGenericDebugContext,
1326                          "xsl:copy: CDATA text %s\n", node->content);
1327                 else
1328                     xsltGenericDebug(xsltGenericDebugContext,
1329                          "xsl:copy: text %s\n", node->content);
1330 #endif
1331                 copy = xmlNewText(node->content);
1332                 if (copy != NULL) {
1333                     if ((node->name == xmlStringTextNoenc) ||
1334                         (node->type == XML_CDATA_SECTION_NODE))
1335                         copy->name = xmlStringTextNoenc;
1336                     xmlAddChild(ctxt->insert, copy);
1337                 } else {
1338                     xsltGenericError(xsltGenericErrorContext,
1339                             "xsl:copy: text copy failed\n");
1340                 }
1341                 break;
1342             case XML_DOCUMENT_NODE:
1343             case XML_HTML_DOCUMENT_NODE:
1344                 break;
1345             case XML_ELEMENT_NODE:
1346 #ifdef WITH_XSLT_DEBUG_PROCESS
1347                 xsltGenericDebug(xsltGenericDebugContext,
1348                                  "xsl:copy: node %s\n", node->name);
1349 #endif
1350                 copy = xsltCopyNode(ctxt, node, ctxt->insert);
1351                 ctxt->insert = copy;
1352                 if (comp->use != NULL) {
1353                     xsltApplyAttributeSet(ctxt, node, inst, comp->use);
1354                 }
1355                 break;
1356             case XML_ATTRIBUTE_NODE: {
1357 #ifdef WITH_XSLT_DEBUG_PROCESS
1358                 xsltGenericDebug(xsltGenericDebugContext,
1359                                  "xsl:copy: attribute %s\n", node->name);
1360 #endif
1361                 if (ctxt->insert->type == XML_ELEMENT_NODE) {
1362                     xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur;
1363                     if (attr->ns != NULL) {
1364                         if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
1365                             (xmlStrncasecmp(attr->ns->prefix,
1366                                             (xmlChar *)"xml", 3))) {
1367                             ret = xmlCopyProp(ctxt->insert, attr);
1368                             ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
1369                                                        ctxt->insert);
1370                         } 
1371                     } else
1372                         ret = xmlCopyProp(ctxt->insert, attr);
1373
1374                     cur = ctxt->insert->properties;
1375                     if (cur != NULL) {
1376                         while (cur->next != NULL)
1377                             cur = cur->next;
1378                         cur->next = ret;
1379                         ret->prev = cur;
1380                     }else
1381                         ctxt->insert->properties = ret;
1382                 }
1383                 break;
1384             }
1385             case XML_PI_NODE:
1386 #ifdef WITH_XSLT_DEBUG_PROCESS
1387                 xsltGenericDebug(xsltGenericDebugContext,
1388                                  "xsl:copy: PI %s\n", node->name);
1389 #endif
1390                 copy = xmlNewPI(node->name, node->content);
1391                 xmlAddChild(ctxt->insert, copy);
1392                 break;
1393             case XML_COMMENT_NODE:
1394 #ifdef WITH_XSLT_DEBUG_PROCESS
1395                 xsltGenericDebug(xsltGenericDebugContext,
1396                                  "xsl:copy: comment\n");
1397 #endif
1398                 copy = xmlNewComment(node->content);
1399                 xmlAddChild(ctxt->insert, copy);
1400                 break;
1401             default:
1402                 break;
1403
1404         }
1405     }
1406
1407     switch (node->type) {
1408         case XML_DOCUMENT_NODE:
1409         case XML_HTML_DOCUMENT_NODE:
1410         case XML_ELEMENT_NODE:
1411             varsPush(ctxt, NULL);
1412             xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
1413             xsltFreeStackElemList(varsPop(ctxt));
1414             break;
1415         default:
1416             break;
1417     }
1418     ctxt->insert = oldInsert;
1419 }
1420
1421 /**
1422  * xsltText:
1423  * @ctxt:  a XSLT process context
1424  * @node:  the node in the source tree.
1425  * @inst:  the xslt text node
1426  * @comp:  precomputed informations
1427  *
1428  * Process the xslt text node on the source node
1429  */
1430 void
1431 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
1432             xmlNodePtr inst, xsltStylePreCompPtr comp) {
1433     if ((inst->children != NULL) && (comp != NULL)) {
1434         xmlNodePtr text = inst->children;
1435         xmlNodePtr copy;
1436
1437         while (text != NULL) {
1438             if (((text->type != XML_TEXT_NODE) &&
1439                  (text->type != XML_CDATA_SECTION_NODE)) ||
1440                 (text->next != NULL)) {
1441                 xsltGenericError(xsltGenericErrorContext,
1442                                  "xsl:text content problem\n");
1443                 break;
1444             }
1445             copy = xmlNewDocText(ctxt->output, text->content);
1446             if ((comp->noescape) || (text->type != XML_CDATA_SECTION_NODE)) {
1447 #ifdef WITH_XSLT_DEBUG_PARSING
1448                 xsltGenericDebug(xsltGenericDebugContext,
1449                      "Disable escaping: %s\n", text->content);
1450 #endif
1451                 copy->name = xmlStringTextNoenc;
1452             }
1453             xmlAddChild(ctxt->insert, copy);
1454             text = text->next;
1455         }
1456     }
1457 }
1458
1459 /**
1460  * xsltElement:
1461  * @ctxt:  a XSLT process context
1462  * @node:  the node in the source tree.
1463  * @inst:  the xslt element node
1464  * @comp:  precomputed informations
1465  *
1466  * Process the xslt element node on the source node
1467  */
1468 void
1469 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
1470             xmlNodePtr inst, xsltStylePreCompPtr comp) {
1471     xmlChar *prop = NULL, *attributes = NULL;
1472     xmlChar *ncname = NULL, *name, *namespace;
1473     xmlChar *prefix = NULL;
1474     xmlChar *value = NULL;
1475     xmlNsPtr ns = NULL, oldns = NULL;
1476     xmlNodePtr copy;
1477     xmlNodePtr oldInsert;
1478
1479
1480     if (ctxt->insert == NULL)
1481         return;
1482     if (!comp->has_name) {
1483         return;
1484     }
1485
1486     /*
1487      * stack and saves
1488      */
1489     oldInsert = ctxt->insert;
1490
1491     if (comp->name == NULL) {
1492         prop = xsltEvalAttrValueTemplate(ctxt, inst,
1493                       (const xmlChar *)"name", XSLT_NAMESPACE);
1494         if (prop == NULL) {
1495             xsltGenericError(xsltGenericErrorContext,
1496                  "xsl:element : name is missing\n");
1497             goto error;
1498         }
1499         name = prop;
1500     } else {
1501         name = comp->name;
1502     }
1503
1504     ncname = xmlSplitQName2(name, &prefix);
1505     if (ncname == NULL) {
1506         prefix = NULL;
1507     } else {
1508         name = ncname;
1509     }
1510
1511     if ((comp->ns == NULL) && (comp->has_ns)) {
1512         namespace = xsltEvalAttrValueTemplate(ctxt, inst,
1513                 (const xmlChar *)"namespace", XSLT_NAMESPACE);
1514         if (namespace != NULL) {
1515             ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
1516                                          ctxt->insert);
1517             xmlFree(namespace);
1518         }
1519     } else if (comp->ns != NULL) {
1520         ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
1521                                      ctxt->insert);
1522     }
1523     if ((ns == NULL) && (prefix != NULL)) {
1524         if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
1525 #ifdef WITH_XSLT_DEBUG_PARSING
1526             xsltGenericDebug(xsltGenericDebugContext,
1527                  "xsl:element : xml prefix forbidden\n");
1528 #endif
1529             goto error;
1530         }
1531         oldns = xmlSearchNs(inst->doc, inst, prefix);
1532         if (oldns == NULL) {
1533             xsltGenericError(xsltGenericErrorContext,
1534                 "xsl:element : no namespace bound to prefix %s\n", prefix);
1535         } else {
1536             ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
1537         }
1538     }
1539
1540     copy = xmlNewDocNode(ctxt->output, ns, name, NULL);
1541     if (copy == NULL) {
1542         xsltGenericError(xsltGenericErrorContext,
1543             "xsl:element : creation of %s failed\n", name);
1544         goto error;
1545     }
1546     if ((ns == NULL) && (oldns != NULL)) {
1547         /* very specific case xsltGetNamespace failed */
1548         ns = xmlNewNs(copy, oldns->href, oldns->prefix);
1549         copy->ns = ns;
1550     }
1551     xmlAddChild(ctxt->insert, copy);
1552     ctxt->insert = copy;
1553     if (comp->has_use) {
1554         if (comp->use != NULL) {
1555             xsltApplyAttributeSet(ctxt, node, inst, comp->use);
1556         } else {
1557             attributes = xsltEvalAttrValueTemplate(ctxt, inst,
1558                        (const xmlChar *)"use-attribute-sets", XSLT_NAMESPACE);
1559             if (attributes != NULL) {
1560                 xsltApplyAttributeSet(ctxt, node, inst, attributes);
1561                 xmlFree(attributes);
1562             }
1563         }
1564     }
1565     
1566     varsPush(ctxt, NULL);
1567     xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
1568     xsltFreeStackElemList(varsPop(ctxt));
1569
1570     ctxt->insert = oldInsert;
1571
1572 error:
1573     if (prop != NULL)
1574         xmlFree(prop);
1575     if (ncname != NULL)
1576         xmlFree(ncname);
1577     if (prefix != NULL)
1578         xmlFree(prefix);
1579     if (value != NULL)
1580         xmlFree(value);
1581 }
1582
1583 /**
1584  * xsltAttribute:
1585  * @ctxt:  a XSLT process context
1586  * @node:  the node in the source tree.
1587  * @inst:  the xslt attribute node
1588  * @comp:  precomputed informations
1589  *
1590  * Process the xslt attribute node on the source node
1591  */
1592 void
1593 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
1594                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
1595     xmlChar *prop = NULL;
1596     xmlChar *ncname = NULL, *name, *namespace;
1597     xmlChar *prefix = NULL;
1598     xmlChar *value = NULL;
1599     xmlNsPtr ns = NULL;
1600     xmlAttrPtr attr;
1601
1602
1603     if (ctxt->insert == NULL)
1604         return;
1605     if (comp == NULL) {
1606         xsltGenericError(xsltGenericErrorContext,
1607              "xsl:attribute : compilation failed\n");
1608         return;
1609     }
1610
1611     if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
1612         return;
1613     if (!comp->has_name) {
1614         return;
1615     }
1616     if (ctxt->insert->children != NULL) {
1617         xsltGenericError(xsltGenericErrorContext,
1618              "xsl:attribute : node has already children\n");
1619         return;
1620     }
1621     if (comp->name == NULL) {
1622         prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name",
1623                                          XSLT_NAMESPACE);
1624         if (prop == NULL) {
1625             xsltGenericError(xsltGenericErrorContext,
1626                  "xsl:attribute : name is missing\n");
1627             goto error;
1628         }
1629         name = prop;
1630     } else {
1631         name = comp->name;
1632     }
1633
1634     ncname = xmlSplitQName2(name, &prefix);
1635     if (ncname == NULL) {
1636         prefix = NULL;
1637     } else {
1638         name = ncname;
1639     }
1640     if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
1641 #ifdef WITH_XSLT_DEBUG_PARSING
1642         xsltGenericDebug(xsltGenericDebugContext,
1643              "xsl:attribute : xml prefix forbidden\n");
1644 #endif
1645         goto error;
1646     }
1647     if ((comp->ns == NULL) && (comp->has_ns)) {
1648         namespace = xsltEvalAttrValueTemplate(ctxt, inst,
1649                 (const xmlChar *)"namespace", XSLT_NAMESPACE);
1650         if (namespace != NULL) {
1651             ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
1652                                          ctxt->insert);
1653             xmlFree(namespace);
1654         } else {
1655             if (prefix != NULL) {
1656                 ns = xmlSearchNs(inst->doc, inst, prefix);
1657                 if (ns == NULL) {
1658                     xsltGenericError(xsltGenericErrorContext,
1659                         "xsl:attribute : no namespace bound to prefix %s\n", prefix);
1660                 } else {
1661                     ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
1662                 }
1663             }
1664         }
1665     } else if (comp->ns != NULL) {
1666         ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
1667                                      ctxt->insert);
1668     }
1669
1670     value = xsltEvalTemplateString(ctxt, node, inst);
1671     if (value == NULL) {
1672         if (ns) {
1673             attr = xmlSetNsProp(ctxt->insert, ns, name, 
1674                                 (const xmlChar *)"");
1675         } else
1676             attr = xmlSetProp(ctxt->insert, name, (const xmlChar *)"");
1677     } else {
1678         if (ns) {
1679             attr = xmlSetNsProp(ctxt->insert, ns, name, value);
1680         } else
1681             attr = xmlSetProp(ctxt->insert, name, value);
1682         
1683     }
1684
1685 error:
1686     if (prop != NULL)
1687         xmlFree(prop);
1688     if (ncname != NULL)
1689         xmlFree(ncname);
1690     if (prefix != NULL)
1691         xmlFree(prefix);
1692     if (value != NULL)
1693         xmlFree(value);
1694 }
1695
1696
1697 /**
1698  * xsltComment:
1699  * @ctxt:  a XSLT process context
1700  * @node:  the node in the source tree.
1701  * @inst:  the xslt comment node
1702  * @comp:  precomputed informations
1703  *
1704  * Process the xslt comment node on the source node
1705  */
1706 void
1707 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
1708                    xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
1709     xmlChar *value = NULL;
1710     xmlNodePtr comment;
1711
1712     value = xsltEvalTemplateString(ctxt, node, inst);
1713     /* TODO: use or generate the compiled form */
1714     /* TODO: check that there is no -- sequence and doesn't end up with - */
1715 #ifdef WITH_XSLT_DEBUG_PROCESS
1716     if (value == NULL)
1717         xsltGenericDebug(xsltGenericDebugContext,
1718              "xsl:comment: empty\n");
1719     else
1720         xsltGenericDebug(xsltGenericDebugContext,
1721              "xsl:comment: content %s\n", value);
1722 #endif
1723
1724     comment = xmlNewComment(value);
1725     xmlAddChild(ctxt->insert, comment);
1726
1727     if (value != NULL)
1728         xmlFree(value);
1729 }
1730
1731 /**
1732  * xsltProcessingInstruction:
1733  * @ctxt:  a XSLT process context
1734  * @node:  the node in the source tree.
1735  * @inst:  the xslt processing-instruction node
1736  * @comp:  precomputed informations
1737  *
1738  * Process the xslt processing-instruction node on the source node
1739  */
1740 void
1741 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
1742                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
1743     xmlChar *ncname = NULL, *name;
1744     xmlChar *value = NULL;
1745     xmlNodePtr pi;
1746
1747
1748     if (ctxt->insert == NULL)
1749         return;
1750     if (comp->has_name == 0)
1751         return;
1752     if (comp->name == NULL) {
1753         ncname = xsltEvalAttrValueTemplate(ctxt, inst,
1754                             (const xmlChar *)"name", XSLT_NAMESPACE);
1755         if (ncname == NULL) {
1756             xsltGenericError(xsltGenericErrorContext,
1757                  "xsl:processing-instruction : name is missing\n");
1758             goto error;
1759         }
1760         name = ncname;
1761     } else {
1762         name = comp->name;
1763     }
1764     /* TODO: check that it's both an an NCName and a PITarget. */
1765
1766
1767     value = xsltEvalTemplateString(ctxt, node, inst);
1768     /* TODO: check that there is no ?> sequence */
1769 #ifdef WITH_XSLT_DEBUG_PROCESS
1770     if (value == NULL)
1771         xsltGenericDebug(xsltGenericDebugContext,
1772              "xsl:processing-instruction: %s empty\n", ncname);
1773     else
1774         xsltGenericDebug(xsltGenericDebugContext,
1775              "xsl:processing-instruction: %s content %s\n", ncname, value);
1776 #endif
1777
1778     pi = xmlNewPI(name, value);
1779     xmlAddChild(ctxt->insert, pi);
1780
1781 error:
1782     if (ncname != NULL)
1783         xmlFree(ncname);
1784     if (value != NULL)
1785         xmlFree(value);
1786 }
1787
1788 /**
1789  * xsltCopyOf:
1790  * @ctxt:  a XSLT process context
1791  * @node:  the node in the source tree.
1792  * @inst:  the xslt copy-of node
1793  * @comp:  precomputed informations
1794  *
1795  * Process the xslt copy-of node on the source node
1796  */
1797 void
1798 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
1799                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
1800     xmlXPathObjectPtr res = NULL;
1801     xmlNodePtr copy = NULL;
1802     xmlNodeSetPtr list = NULL;
1803     int i;
1804     int oldProximityPosition, oldContextSize;
1805
1806     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1807         return;
1808     if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
1809         xsltGenericError(xsltGenericErrorContext,
1810              "xsl:copy-of : compilation failed\n");
1811         return;
1812     }
1813
1814 #ifdef WITH_XSLT_DEBUG_PROCESS
1815     xsltGenericDebug(xsltGenericDebugContext,
1816          "xsltCopyOf: select %s\n", comp->select);
1817 #endif
1818
1819     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1820     oldContextSize = ctxt->xpathCtxt->contextSize;
1821     ctxt->xpathCtxt->node = node;
1822     ctxt->xpathCtxt->namespaces = comp->nsList;
1823     ctxt->xpathCtxt->nsNr = comp->nsNr;
1824     res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1825     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1826     ctxt->xpathCtxt->contextSize = oldContextSize;
1827     if (res != NULL) {
1828         if (res->type == XPATH_NODESET) {
1829 #ifdef WITH_XSLT_DEBUG_PROCESS
1830             xsltGenericDebug(xsltGenericDebugContext,
1831                  "xsltCopyOf: result is a node set\n");
1832 #endif
1833             list = res->nodesetval;
1834             if (list != NULL) {
1835                 /* sort the list in document order */
1836                 xsltDocumentSortFunction(list);
1837                 /* append everything in this order under ctxt->insert */
1838                 for (i = 0;i < list->nodeNr;i++) {
1839                     if (list->nodeTab[i] == NULL)
1840                         continue;
1841                     if ((list->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1842                         (list->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) {
1843                         xsltCopyTreeList(ctxt, list->nodeTab[i]->children,
1844                                          ctxt->insert);
1845                     } else if (list->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
1846                         xsltCopyProp(ctxt, ctxt->insert, 
1847                                      (xmlAttrPtr) list->nodeTab[i]);
1848                     } else {
1849                         xsltCopyTree(ctxt, list->nodeTab[i], ctxt->insert);
1850                     }
1851                 }
1852             }
1853         } else if (res->type == XPATH_XSLT_TREE) {
1854 #ifdef WITH_XSLT_DEBUG_PROCESS
1855             xsltGenericDebug(xsltGenericDebugContext,
1856                  "xsltCopyOf: result is a result tree fragment\n");
1857 #endif
1858             list = res->nodesetval;
1859             if ((list != NULL) && (list->nodeTab != NULL) &&
1860                 (list->nodeTab[0] != NULL)) {
1861                 xsltCopyTreeList(ctxt, list->nodeTab[0]->children,
1862                                  ctxt->insert);
1863             }
1864         } else {
1865             /* convert to a string */
1866             res = xmlXPathConvertString(res);
1867             if ((res != NULL) && (res->type == XPATH_STRING)) {
1868                 /* append content as text node */
1869                 copy = xmlNewText(res->stringval);
1870                 if (copy != NULL) {
1871                     xmlAddChild(ctxt->insert, copy);
1872                 }
1873             }
1874             if (copy == NULL) {
1875                 xsltGenericError(xsltGenericErrorContext,
1876                     "xsltCopyOf: text copy failed\n");
1877             }
1878 #ifdef WITH_XSLT_DEBUG_PROCESS
1879             else
1880                 xsltGenericDebug(xsltGenericDebugContext,
1881                      "xsltCopyOf: result %s\n", res->stringval);
1882 #endif
1883         }
1884     }
1885
1886     if (res != NULL)
1887         xmlXPathFreeObject(res);
1888 }
1889
1890 /**
1891  * xsltValueOf:
1892  * @ctxt:  a XSLT process context
1893  * @node:  the node in the source tree.
1894  * @inst:  the xslt value-of node
1895  * @comp:  precomputed informations
1896  *
1897  * Process the xslt value-of node on the source node
1898  */
1899 void
1900 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
1901                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
1902     xmlXPathObjectPtr res = NULL;
1903     xmlNodePtr copy = NULL;
1904     int oldProximityPosition, oldContextSize;
1905
1906     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1907         return;
1908     if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
1909         xsltGenericError(xsltGenericErrorContext,
1910              "xsl:value-of : compilation failed\n");
1911         return;
1912     }
1913
1914 #ifdef WITH_XSLT_DEBUG_PROCESS
1915     xsltGenericDebug(xsltGenericDebugContext,
1916          "xsltValueOf: select %s\n", comp->select);
1917 #endif
1918
1919     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1920     oldContextSize = ctxt->xpathCtxt->contextSize;
1921     ctxt->xpathCtxt->node = node;
1922     ctxt->xpathCtxt->namespaces = comp->nsList;
1923     ctxt->xpathCtxt->nsNr = comp->nsNr;
1924     res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1925     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1926     ctxt->xpathCtxt->contextSize = oldContextSize;
1927     if (res != NULL) {
1928         if (res->type != XPATH_STRING)
1929             res = xmlXPathConvertString(res);
1930         if (res->type == XPATH_STRING) {
1931             copy = xmlNewText(res->stringval);
1932             if (copy != NULL) {
1933                 if (comp->noescape)
1934                     copy->name = xmlStringTextNoenc;
1935                 xmlAddChild(ctxt->insert, copy);
1936             }
1937         }
1938     }
1939     if (copy == NULL) {
1940         xsltGenericError(xsltGenericErrorContext,
1941             "xsltValueOf: text copy failed\n");
1942     }
1943 #ifdef WITH_XSLT_DEBUG_PROCESS
1944     else
1945         xsltGenericDebug(xsltGenericDebugContext,
1946              "xsltValueOf: result %s\n", res->stringval);
1947 #endif
1948     if (res != NULL)
1949         xmlXPathFreeObject(res);
1950 }
1951
1952 /**
1953  * xsltNumber:
1954  * @ctxt:  a XSLT process context
1955  * @node:  the node in the source tree.
1956  * @inst:  the xslt number node
1957  * @comp:  precomputed informations
1958  *
1959  * Process the xslt number node on the source node
1960  */
1961 void
1962 xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
1963            xmlNodePtr inst, xsltStylePreCompPtr comp)
1964 {
1965     if (comp == NULL) {
1966         xsltGenericError(xsltGenericErrorContext,
1967              "xsl:number : compilation failed\n");
1968         return;
1969     }
1970
1971     if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
1972         return;
1973
1974     comp->numdata.doc = inst->doc;
1975     comp->numdata.node = inst;
1976     
1977     xsltNumberFormat(ctxt, &comp->numdata, node);
1978 }
1979
1980 /**
1981  * xsltApplyImports:
1982  * @ctxt:  a XSLT process context
1983  * @node:  the node in the source tree.
1984  * @inst:  the xslt apply-imports node
1985  * @comp:  precomputed informations
1986  *
1987  * Process the xslt apply-imports node on the source node
1988  */
1989 void
1990 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr node,
1991                  xmlNodePtr inst ATTRIBUTE_UNUSED, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
1992     xsltTemplatePtr template;
1993
1994     if ((ctxt->templ == NULL) || (ctxt->templ->style == NULL)) {
1995         xsltGenericError(xsltGenericErrorContext,
1996              "xsl:apply-imports : internal error no current template\n");
1997         return;
1998     }
1999     template = xsltGetTemplate(ctxt, node, ctxt->templ->style);
2000     if (template != NULL) {
2001         templPush(ctxt, template);
2002         varsPush(ctxt, NULL);
2003         xsltApplyOneTemplate(ctxt, node, template->content, 1);
2004         xsltFreeStackElemList(varsPop(ctxt));
2005         templPop(ctxt);
2006     }
2007 }
2008
2009 /**
2010  * xsltCallTemplate:
2011  * @ctxt:  a XSLT process context
2012  * @node:  the node in the source tree.
2013  * @inst:  the xslt call-template node
2014  * @comp:  precomputed informations
2015  *
2016  * Process the xslt call-template node on the source node
2017  */
2018 void
2019 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2020                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
2021     xmlNodePtr cur = NULL;
2022     xsltStackElemPtr params = NULL, param;
2023
2024
2025     if (ctxt->insert == NULL)
2026         return;
2027     if (comp == NULL) {
2028         xsltGenericError(xsltGenericErrorContext,
2029              "xsl:call-template : compilation failed\n");
2030         return;
2031     }
2032
2033     /*
2034      * The template must have been precomputed
2035      */
2036     if (comp->templ == NULL) {
2037         comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
2038         if (comp->templ == NULL) {
2039             xsltGenericError(xsltGenericErrorContext,
2040                  "xsl:call-template : template %s not found\n", comp->name);
2041             return;
2042         }
2043     }
2044
2045     /*
2046      * Create a new frame but block access to variables
2047      */
2048     templPush(ctxt, comp->templ);
2049     cur = inst->children;
2050     while (cur != NULL) {
2051         if (ctxt->state == XSLT_STATE_STOPPED) break;
2052         if (IS_XSLT_ELEM(cur)) {
2053             if (IS_XSLT_NAME(cur, "with-param")) {
2054                 param = xsltParseStylesheetCallerParam(ctxt, cur);
2055                 if (param != NULL) {
2056                     param->next = params;
2057                     params = param;
2058                 }
2059             } else {
2060                 xsltGenericError(xsltGenericDebugContext,
2061                      "xsl:call-template: misplaced xsl:%s\n", cur->name);
2062             }
2063         } else {
2064             xsltGenericError(xsltGenericDebugContext,
2065                  "xsl:call-template: misplaced %s element\n", cur->name);
2066         }
2067         cur = cur->next;
2068     }
2069     varsPush(ctxt, params);
2070     xsltApplyOneTemplate(ctxt, node, comp->templ->content, 1);
2071     xsltFreeStackElemList(varsPop(ctxt));
2072     templPop(ctxt);
2073 }
2074
2075 /**
2076  * xsltApplyTemplates:
2077  * @ctxt:  a XSLT process context
2078  * @node:  the node in the source tree.
2079  * @inst:  the apply-templates node
2080  * @comp:  precomputed informations
2081  *
2082  * Process the apply-templates node on the source node
2083  */
2084 void
2085 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
2086                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
2087     xmlNodePtr cur, delete = NULL, oldNode;
2088     xmlXPathObjectPtr res = NULL;
2089     xmlNodeSetPtr list = NULL, oldList;
2090     int i, oldProximityPosition, oldContextSize;
2091     const xmlChar *oldMode, *oldModeURI;
2092     xsltStackElemPtr params = NULL, param, tmp, p;
2093     int nbsorts = 0;
2094     int newdoc = 0;
2095     xmlNodePtr sorts[XSLT_MAX_SORT];
2096     xmlDocPtr oldXDocPtr, newXDocPtr;
2097     xsltDocumentPtr oldCDocPtr, newCDocPtr;
2098
2099     if (comp == NULL) {
2100         xsltGenericError(xsltGenericErrorContext,
2101              "xsl:apply-templates : compilation failed\n");
2102         return;
2103     }
2104     if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
2105         return;
2106
2107 #ifdef WITH_XSLT_DEBUG_PROCESS
2108     if ((node != NULL) && (node->name != NULL))
2109         xsltGenericDebug(xsltGenericDebugContext,
2110              "xsltApplyTemplates: node: %s\n", node->name);
2111 #endif
2112
2113     /*
2114      * Get mode if any
2115      */
2116     oldNode = ctxt->node;
2117     oldMode = ctxt->mode;
2118     oldModeURI = ctxt->modeURI;
2119     ctxt->mode = comp->mode;
2120     ctxt->modeURI = comp->modeURI;
2121
2122     /*
2123      * The xpath context size and proximity position, as
2124      * well as the xpath and context documents, may be changed
2125      * so we save their initial state and will restore on exit
2126      */
2127     newXDocPtr = oldXDocPtr = ctxt->xpathCtxt->doc;
2128     newCDocPtr = oldCDocPtr = ctxt->document;
2129     oldContextSize = ctxt->xpathCtxt->contextSize;
2130     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2131
2132     if (comp->select != NULL) {
2133         if (comp->comp == NULL) {
2134             xsltGenericError(xsltGenericErrorContext,
2135                  "xsl:apply-templates : compilation failed\n");
2136             goto error;
2137         }
2138 #ifdef WITH_XSLT_DEBUG_PROCESS
2139         xsltGenericDebug(xsltGenericDebugContext,
2140              "xsltApplyTemplates: select %s\n", comp->select);
2141 #endif
2142
2143         ctxt->xpathCtxt->node = node;
2144         ctxt->xpathCtxt->namespaces = comp->nsList;
2145         ctxt->xpathCtxt->nsNr = comp->nsNr;
2146         res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
2147         ctxt->xpathCtxt->contextSize = oldContextSize;
2148         ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2149         if (res != NULL) {
2150             if (res->type == XPATH_NODESET) {
2151                 list = res->nodesetval;
2152                 /* For a 'select' nodeset, need to check if document has changed */
2153                 if ( (res->nodesetval!=NULL) &&
2154                      (res->nodesetval->nodeTab!=NULL) &&
2155                      (res->nodesetval->nodeTab[0]->doc!=NULL) &&
2156                      (res->nodesetval->nodeTab[0]->doc->doc!=NULL) &&
2157                      (res->nodesetval->nodeTab[0]->doc->doc)!=ctxt->xpathCtxt->doc) {     
2158                     newXDocPtr=res->nodesetval->nodeTab[0]->doc->doc;
2159                     /* The nodeset is from another document, so must change */
2160                     if ((newCDocPtr = xsltFindDocument(ctxt,newXDocPtr))==NULL) { 
2161                         xsltGenericError(xsltGenericErrorContext,
2162                                 "xsl:apply-templates : can't find doc\n");
2163                         goto error;
2164                     }
2165                     /* Can't actually do the change yet, so set flag for later */
2166                     newdoc = 1;
2167                 }
2168                 res->nodesetval = NULL;
2169              } else {
2170                 list = NULL;
2171              }
2172         }
2173         if (list == NULL) {
2174 #ifdef WITH_XSLT_DEBUG_PROCESS
2175             xsltGenericDebug(xsltGenericDebugContext,
2176                 "xsltApplyTemplates: select didn't evaluate to a node list\n");
2177 #endif
2178             goto error;
2179         }
2180     } else {
2181         /*
2182          * Build an XPath nodelist with the children
2183          */
2184         list = xmlXPathNodeSetCreate(NULL);
2185         cur = node->children;
2186         while (cur != NULL) {
2187             switch (cur->type) {
2188                 case XML_TEXT_NODE:
2189                     if ((IS_BLANK_NODE(cur)) &&
2190                         (cur->parent != NULL) &&
2191                         (ctxt->style->stripSpaces != NULL)) {
2192                         const xmlChar *val;
2193
2194                         val = (const xmlChar *)
2195                               xmlHashLookup(ctxt->style->stripSpaces,
2196                                             cur->parent->name);
2197                         if ((val != NULL) &&
2198                             (xmlStrEqual(val, (xmlChar *) "strip"))) {
2199                             delete = cur;
2200                             break;
2201                         }
2202                     }
2203                     /* no break on purpose */
2204                 case XML_DOCUMENT_NODE:
2205                 case XML_HTML_DOCUMENT_NODE:
2206                 case XML_ELEMENT_NODE:
2207                 case XML_CDATA_SECTION_NODE:
2208                 case XML_PI_NODE:
2209                 case XML_COMMENT_NODE:
2210                     xmlXPathNodeSetAdd(list, cur);
2211                     break;
2212                 default:
2213 #ifdef WITH_XSLT_DEBUG_PROCESS
2214                     xsltGenericDebug(xsltGenericDebugContext,
2215                      "xsltApplyTemplates: skipping cur type %d\n",
2216                                      cur->type);
2217 #endif
2218                     delete = cur;
2219             }
2220             cur = cur->next;
2221             if (delete != NULL) {
2222 #ifdef WITH_XSLT_DEBUG_PROCESS
2223                 xsltGenericDebug(xsltGenericDebugContext,
2224                      "xsltApplyTemplates: removing ignorable blank cur\n");
2225 #endif
2226                 xmlUnlinkNode(delete);
2227                 xmlFreeNode(delete);
2228                 delete = NULL;
2229             }
2230         }
2231     }
2232
2233 #ifdef WITH_XSLT_DEBUG_PROCESS
2234     if (list != NULL)
2235     xsltGenericDebug(xsltGenericDebugContext,
2236         "xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
2237 #endif
2238
2239     oldList = ctxt->nodeList;
2240     ctxt->nodeList = list;
2241     ctxt->xpathCtxt->contextSize = list->nodeNr;
2242
2243     /* 
2244      * handle (or skip) the xsl:sort and xsl:with-param
2245      */
2246     cur = inst->children;
2247     while (cur!=NULL) {
2248         if (ctxt->state == XSLT_STATE_STOPPED) break;
2249         if (IS_XSLT_ELEM(cur)) {
2250             if (IS_XSLT_NAME(cur, "with-param")) {
2251                 param = xsltParseStylesheetCallerParam(ctxt, cur);
2252                 if (param != NULL) {
2253                     param->next = params;
2254                     params = param;
2255                 }
2256             } else if (IS_XSLT_NAME(cur, "sort")) {
2257                 if (nbsorts >= XSLT_MAX_SORT) {
2258                     xsltGenericError(xsltGenericDebugContext,
2259                         "xsl:call-template: %s too many sort\n", node->name);
2260                 } else {
2261                     sorts[nbsorts++] = cur;
2262                 }
2263             } else {
2264                 xsltGenericError(xsltGenericDebugContext,
2265                     "xsl:call-template: misplaced xsl:%s\n", cur->name);
2266             }
2267         } else {
2268             xsltGenericError(xsltGenericDebugContext,
2269                  "xsl:call-template: misplaced %s element\n", cur->name);
2270         }
2271         cur = cur->next;
2272     }
2273
2274     if (nbsorts > 0) {
2275         xsltDoSortFunction(ctxt, sorts, nbsorts);
2276     }
2277
2278
2279     /* The original 'select' may have required a change of document*/
2280     if (newdoc) {
2281 #ifdef WITH_XSLT_DEBUG_PROCESS
2282         xsltGenericDebug(xsltGenericDebugContext,
2283      "xsltApplyTemplates: Changing document - context doc %s, xpathdoc %s\n",
2284              newCDocPtr->doc->URL, newXDocPtr->URL);
2285 #endif
2286
2287         ctxt->document = newCDocPtr;
2288         ctxt->xpathCtxt->doc = newXDocPtr;
2289         ctxt->xpathCtxt->node = list->nodeTab[0];
2290     }
2291
2292     for (i = 0;i < list->nodeNr;i++) {
2293         ctxt->node = list->nodeTab[i];
2294         ctxt->xpathCtxt->proximityPosition = i + 1;
2295         varsPush(ctxt, params);
2296         xsltProcessOneNode(ctxt, list->nodeTab[i]);
2297         tmp = varsPop(ctxt);
2298         /*
2299          * Free other parameter and variables which may have been
2300          * added to the set defined in the caller.
2301          */
2302         if (params == NULL) {
2303             xsltFreeStackElemList(tmp);
2304         } else if (tmp != params) {
2305             p = tmp;
2306             while ((p != NULL) && (p->next != params))
2307                 p = p->next;
2308             if (p == NULL) {
2309                 xsltFreeStackElemList(tmp);
2310             } else {
2311                 p->next = NULL;
2312                 xsltFreeStackElemList(tmp);
2313             }
2314         }
2315     }
2316     xsltFreeStackElemList(params);      /* free the parameter list */
2317     ctxt->nodeList = oldList;
2318     ctxt->xpathCtxt->contextSize = oldContextSize;
2319     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2320     ctxt->xpathCtxt->doc = oldXDocPtr;
2321     ctxt->document = oldCDocPtr;
2322
2323 error:
2324     ctxt->node = oldNode;
2325     ctxt->mode = oldMode;
2326     ctxt->modeURI = oldModeURI;
2327     if (res != NULL)
2328         xmlXPathFreeObject(res);
2329     if (list != NULL)
2330         xmlXPathFreeNodeSet(list);
2331 }
2332
2333
2334 /**
2335  * xsltChoose:
2336  * @ctxt:  a XSLT process context
2337  * @node:  the node in the source tree.
2338  * @inst:  the xslt choose node
2339  * @comp:  precomputed informations
2340  *
2341  * Process the xslt choose node on the source node
2342  */
2343 void
2344 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node,
2345            xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
2346     xmlChar *prop = NULL;
2347     xmlXPathObjectPtr res = NULL;
2348     xmlNodePtr replacement, when;
2349     int doit = 1;
2350     int oldProximityPosition, oldContextSize;
2351
2352     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2353         return;
2354
2355     /* 
2356      * Check the when's
2357      */
2358     replacement = inst->children;
2359     if (replacement == NULL) {
2360         xsltGenericError(xsltGenericErrorContext,
2361              "xsl:choose: empty content not allowed\n");
2362         goto error;
2363     }
2364     if ((!IS_XSLT_ELEM(replacement)) ||
2365         (!IS_XSLT_NAME(replacement, "when"))) {
2366         xsltGenericError(xsltGenericErrorContext,
2367              "xsl:choose: xsl:when expected first\n");
2368         goto error;
2369     }
2370     while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "when"))) {
2371         xsltStylePreCompPtr wcomp = replacement->_private;
2372
2373         if ((wcomp == NULL) || (wcomp->test == NULL) || (wcomp->comp == NULL)) {
2374             xsltGenericError(xsltGenericErrorContext,
2375                  "xsl:when: compilation failed !\n");
2376             goto error;
2377         }
2378         when = replacement;
2379 #ifdef WITH_XSLT_DEBUG_PROCESS
2380         xsltGenericDebug(xsltGenericDebugContext,
2381              "xsl:when: test %s\n", wcomp->test);
2382 #endif
2383
2384         oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2385         oldContextSize = ctxt->xpathCtxt->contextSize;
2386         ctxt->xpathCtxt->node = node;
2387         ctxt->xpathCtxt->namespaces = comp->nsList;
2388         ctxt->xpathCtxt->nsNr = comp->nsNr;
2389         res = xmlXPathCompiledEval(wcomp->comp, ctxt->xpathCtxt);
2390         ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2391         ctxt->xpathCtxt->contextSize = oldContextSize;
2392         if (res != NULL) {
2393             if (res->type != XPATH_BOOLEAN)
2394                 res = xmlXPathConvertBoolean(res);
2395             if (res->type == XPATH_BOOLEAN)
2396                 doit = res->boolval;
2397             else {
2398 #ifdef WITH_XSLT_DEBUG_PROCESS
2399                 xsltGenericDebug(xsltGenericDebugContext,
2400                     "xsl:when: test didn't evaluate to a boolean\n");
2401 #endif
2402                 goto error;
2403             }
2404         }
2405
2406 #ifdef WITH_XSLT_DEBUG_PROCESS
2407         xsltGenericDebug(xsltGenericDebugContext,
2408             "xsl:when: test evaluate to %d\n", doit);
2409 #endif
2410         if (doit) {
2411             varsPush(ctxt, NULL);
2412             xsltApplyOneTemplate(ctxt, ctxt->node, when->children, 0);
2413             xsltFreeStackElemList(varsPop(ctxt));
2414             goto done;
2415         }
2416         if (prop != NULL)
2417             xmlFree(prop);
2418         prop = NULL;
2419         if (res != NULL)
2420             xmlXPathFreeObject(res);
2421         res = NULL;
2422         replacement = replacement->next;
2423     }
2424     if (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "otherwise"))) {
2425         varsPush(ctxt, NULL);
2426         xsltApplyOneTemplate(ctxt, ctxt->node, replacement->children, 0);
2427         xsltFreeStackElemList(varsPop(ctxt));
2428         replacement = replacement->next;
2429     }
2430     if (replacement != NULL) {
2431         xsltGenericError(xsltGenericErrorContext,
2432              "xsl:choose: unexpected content %s\n", replacement->name);
2433         goto error;
2434     }
2435
2436 done:
2437 error:
2438     if (prop != NULL)
2439         xmlFree(prop);
2440     if (res != NULL)
2441         xmlXPathFreeObject(res);
2442 }
2443
2444 /**
2445  * xsltIf:
2446  * @ctxt:  a XSLT process context
2447  * @node:  the node in the source tree.
2448  * @inst:  the xslt if node
2449  * @comp:  precomputed informations
2450  *
2451  * Process the xslt if node on the source node
2452  */
2453 void
2454 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
2455                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
2456     xmlXPathObjectPtr res = NULL;
2457     int doit = 1;
2458     int oldContextSize, oldProximityPosition;
2459
2460     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2461         return;
2462     if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
2463         xsltGenericError(xsltGenericErrorContext,
2464              "xsl:if : compilation failed\n");
2465         return;
2466     }
2467
2468 #ifdef WITH_XSLT_DEBUG_PROCESS
2469     xsltGenericDebug(xsltGenericDebugContext,
2470          "xsltIf: test %s\n", comp->test);
2471 #endif
2472
2473     oldContextSize = ctxt->xpathCtxt->contextSize;
2474     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2475     ctxt->xpathCtxt->node = node;
2476     ctxt->xpathCtxt->namespaces = comp->nsList;
2477     ctxt->xpathCtxt->nsNr = comp->nsNr;
2478     res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
2479     ctxt->xpathCtxt->contextSize = oldContextSize;
2480     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2481     if (res != NULL) {
2482         if (res->type != XPATH_BOOLEAN)
2483             res = xmlXPathConvertBoolean(res);
2484         if (res->type == XPATH_BOOLEAN)
2485             doit = res->boolval;
2486         else {
2487 #ifdef WITH_XSLT_DEBUG_PROCESS
2488             xsltGenericDebug(xsltGenericDebugContext,
2489                 "xsltIf: test didn't evaluate to a boolean\n");
2490 #endif
2491             goto error;
2492         }
2493     }
2494
2495 #ifdef WITH_XSLT_DEBUG_PROCESS
2496     xsltGenericDebug(xsltGenericDebugContext,
2497         "xsltIf: test evaluate to %d\n", doit);
2498 #endif
2499     if (doit) {
2500         varsPush(ctxt, NULL);
2501         xsltApplyOneTemplate(ctxt, node, inst->children, 0);
2502         xsltFreeStackElemList(varsPop(ctxt));
2503     }
2504
2505 error:
2506     if (res != NULL)
2507         xmlXPathFreeObject(res);
2508 }
2509
2510 /**
2511  * xsltForEach:
2512  * @ctxt:  a XSLT process context
2513  * @node:  the node in the source tree.
2514  * @inst:  the xslt for-each node
2515  * @comp:  precomputed informations
2516  *
2517  * Process the xslt for-each node on the source node
2518  */
2519 void
2520 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
2521                    xmlNodePtr inst, xsltStylePreCompPtr comp) {
2522     xmlXPathObjectPtr res = NULL;
2523     xmlNodePtr replacement;
2524     xmlNodeSetPtr list = NULL, oldList;
2525     int i, oldProximityPosition, oldContextSize;
2526     xmlNodePtr oldNode = ctxt->node;
2527     int nbsorts = 0;
2528     xmlNodePtr sorts[XSLT_MAX_SORT];
2529     xmlDocPtr oldXDocPtr, newXDocPtr;
2530     xsltDocumentPtr oldCDocPtr, newCDocPtr;
2531
2532     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2533         return;
2534     if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
2535         xsltGenericError(xsltGenericErrorContext,
2536              "xsl:for-each : compilation failed\n");
2537         return;
2538     }
2539
2540 #ifdef WITH_XSLT_DEBUG_PROCESS
2541     xsltGenericDebug(xsltGenericDebugContext,
2542          "xsltForEach: select %s\n", comp->select);
2543 #endif
2544
2545     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2546     oldContextSize = ctxt->xpathCtxt->contextSize;
2547     ctxt->xpathCtxt->node = node;
2548     ctxt->xpathCtxt->namespaces = comp->nsList;
2549     ctxt->xpathCtxt->nsNr = comp->nsNr;
2550     oldCDocPtr = ctxt->document;
2551     oldXDocPtr = ctxt->xpathCtxt->doc;
2552     res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
2553     ctxt->xpathCtxt->contextSize = oldContextSize;
2554     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2555     if (res != NULL) {
2556         if (res->type == XPATH_NODESET)
2557             list = res->nodesetval;
2558     }
2559     if (list == NULL) {
2560 #ifdef WITH_XSLT_DEBUG_PROCESS
2561         xsltGenericDebug(xsltGenericDebugContext,
2562             "xsltForEach: select didn't evaluate to a node list\n");
2563 #endif
2564         goto error;
2565     }
2566
2567 #ifdef WITH_XSLT_DEBUG_PROCESS
2568     xsltGenericDebug(xsltGenericDebugContext,
2569         "xsltForEach: select evaluates to %d nodes\n", list->nodeNr);
2570 #endif
2571
2572     oldList = ctxt->nodeList;
2573     ctxt->nodeList = list;
2574     oldContextSize = ctxt->xpathCtxt->contextSize;
2575     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2576     ctxt->xpathCtxt->contextSize = list->nodeNr;
2577
2578     /* For a 'select' nodeset, need to check if document has changed */
2579     if (list->nodeTab!=NULL) {
2580         if ( (res->nodesetval!=NULL) &&
2581              (res->nodesetval->nodeTab!=NULL) &&
2582              (res->nodesetval->nodeTab[0]->doc!=NULL) &&
2583              (res->nodesetval->nodeTab[0]->doc->doc!=NULL) &&
2584              (res->nodesetval->nodeTab[0]->doc->doc)!=ctxt->xpathCtxt->doc) {     
2585             newXDocPtr=res->nodesetval->nodeTab[0]->doc->doc;
2586             /* The nodeset is from another document, so must change */
2587             if ((newCDocPtr = xsltFindDocument(ctxt,newXDocPtr))==NULL) {
2588                 xsltGenericError(xsltGenericErrorContext,
2589                         "xsl:for-each : can't find document\n");
2590                 goto error;
2591             }
2592             ctxt->document = newCDocPtr;
2593             ctxt->xpathCtxt->doc = newXDocPtr;
2594             ctxt->xpathCtxt->node = list->nodeTab[0];
2595             ctxt->xpathCtxt->contextSize = list->nodeNr;
2596             ctxt->xpathCtxt->proximityPosition = 0;
2597         }
2598     }
2599
2600     /* 
2601      * handle and skip the xsl:sort
2602      */
2603     replacement = inst->children;
2604     while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
2605         if (nbsorts >= XSLT_MAX_SORT) {
2606             xsltGenericError(xsltGenericDebugContext,
2607                 "xsl:for-each: too many sort\n");
2608         } else {
2609             sorts[nbsorts++] = replacement;
2610         }
2611         replacement = replacement->next;
2612     }
2613
2614     if (nbsorts > 0) {
2615         xsltDoSortFunction(ctxt, sorts, nbsorts);
2616     }
2617
2618
2619     for (i = 0;i < list->nodeNr;i++) {
2620         ctxt->node = list->nodeTab[i];
2621         ctxt->xpathCtxt->proximityPosition = i + 1;
2622         /* ctxt->insert = oldInsert; */
2623         varsPush(ctxt, NULL);
2624         xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement, 0);
2625         xsltFreeStackElemList(varsPop(ctxt));
2626     }
2627     ctxt->document = oldCDocPtr;
2628     ctxt->nodeList = oldList;
2629     ctxt->node = oldNode;
2630     ctxt->xpathCtxt->doc = oldXDocPtr;
2631     ctxt->xpathCtxt->contextSize = oldContextSize;
2632     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2633
2634 error:
2635     if (res != NULL)
2636         xmlXPathFreeObject(res);
2637 }
2638
2639 /************************************************************************
2640  *                                                                      *
2641  *                      Generic interface                               *
2642  *                                                                      *
2643  ************************************************************************/
2644
2645 #ifdef XSLT_GENERATE_HTML_DOCTYPE
2646 typedef struct xsltHTMLVersion {
2647     const char *version;
2648     const char *public;
2649     const char *system;
2650 } xsltHTMLVersion;
2651
2652 static xsltHTMLVersion xsltHTMLVersions[] = {
2653     { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
2654       "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
2655     { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
2656       "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
2657     { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
2658       "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
2659     { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
2660       "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
2661     { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
2662       "http://www.w3.org/TR/html4/strict.dtd"},
2663     { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
2664       "http://www.w3.org/TR/html4/loose.dtd"},
2665     { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
2666       "http://www.w3.org/TR/html4/frameset.dtd"},
2667     { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
2668       "http://www.w3.org/TR/html4/loose.dtd"},
2669     { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
2670 };
2671
2672 /**
2673  * xsltGetHTMLIDs:
2674  * @version:  the version string
2675  * @public:  used to return the public ID
2676  * @system:  used to return the system ID
2677  *
2678  * Returns -1 if not found, 0 otherwise and the system and public
2679  *         Identifier for this given verion of HTML
2680  */
2681 static int
2682 xsltGetHTMLIDs(const xmlChar *version, const xmlChar **public,
2683                     const xmlChar **system) {
2684     unsigned int i;
2685     if (version == NULL)
2686         return(-1);
2687     for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
2688          i++) {
2689         if (!xmlStrcasecmp(version,
2690                            (const xmlChar *) xsltHTMLVersions[i].version)) {
2691             if (public != NULL)
2692                 *public = (const xmlChar *) xsltHTMLVersions[i].public;
2693             if (system != NULL)
2694                 *system = (const xmlChar *) xsltHTMLVersions[i].system;
2695             return(0);
2696         }
2697     }
2698     return(-1);
2699 }
2700 #endif
2701
2702 /**
2703  * xsltApplyStylesheet:
2704  * @style:  a parsed XSLT stylesheet
2705  * @doc:  a parsed XML document
2706  * @params:  a NULL terminated arry of parameters names/values tuples
2707  *
2708  * Apply the stylesheet to the document
2709  * NOTE: This may lead to a non-wellformed output XML wise !
2710  *
2711  * Returns the result document or NULL in case of error
2712  */
2713 xmlDocPtr
2714 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
2715                     const char **params) {
2716     xmlDocPtr res = NULL;
2717     xsltTransformContextPtr ctxt = NULL;
2718     xmlNodePtr root;
2719     const xmlChar *method;
2720     const xmlChar *doctypePublic;
2721     const xmlChar *doctypeSystem;
2722     const xmlChar *version;
2723     xsltStackElemPtr variables;
2724     xsltStackElemPtr vptr;
2725
2726
2727     if ((style == NULL) || (doc == NULL))
2728         return(NULL);
2729     ctxt = xsltNewTransformContext(style, doc);
2730     xsltRegisterExtras(ctxt);
2731     if (ctxt == NULL)
2732         return(NULL);
2733
2734     XSLT_GET_IMPORT_PTR(method, style, method)
2735     XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
2736     XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
2737     XSLT_GET_IMPORT_PTR(version, style, version)
2738
2739     if ((method != NULL) &&
2740         (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
2741         if (xmlStrEqual(method, (const xmlChar *) "html")) {
2742             ctxt->type = XSLT_OUTPUT_HTML;
2743             if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
2744                 res = htmlNewDoc(doctypeSystem, doctypePublic);
2745             else {
2746                 if (version == NULL)
2747                     version = (const xmlChar *) "4.0";
2748 #ifdef XSLT_GENERATE_HTML_DOCTYPE
2749                 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
2750 #endif
2751                 res = htmlNewDoc(doctypeSystem, doctypePublic);
2752             }
2753             if (res == NULL)
2754                 goto error;
2755         } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
2756             xsltGenericError(xsltGenericErrorContext,
2757              "xsltApplyStylesheet: unsupported method xhtml, using html\n",
2758                              style->method);
2759             ctxt->type = XSLT_OUTPUT_HTML;
2760             res = htmlNewDoc(doctypeSystem, doctypePublic);
2761             if (res == NULL)
2762                 goto error;
2763         } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
2764             ctxt->type = XSLT_OUTPUT_TEXT;
2765             res = xmlNewDoc(style->version);
2766             if (res == NULL)
2767                 goto error;
2768         } else {
2769             xsltGenericError(xsltGenericErrorContext,
2770                              "xsltApplyStylesheet: unsupported method %s\n",
2771                              style->method);
2772             goto error;
2773         }
2774     } else {
2775         ctxt->type = XSLT_OUTPUT_XML;
2776         res = xmlNewDoc(style->version);
2777         if (res == NULL)
2778             goto error;
2779     }
2780     res->charset = XML_CHAR_ENCODING_UTF8;
2781     if (style->encoding != NULL)
2782         res->encoding = xmlStrdup(style->encoding);
2783     variables = style->variables;
2784
2785     /*
2786      * Start the evaluation, evaluate the params, the stylesheets globals
2787      * and start by processing the top node.
2788      */
2789     ctxt->output = res;
2790     ctxt->insert = (xmlNodePtr) res;
2791     ctxt->globalVars = xmlHashCreate(20);
2792     if (params != NULL)
2793         xsltEvalUserParams(ctxt, params);
2794     xsltEvalGlobalVariables(ctxt);
2795     ctxt->node = (xmlNodePtr) doc;
2796     varsPush(ctxt, NULL);
2797     xsltProcessOneNode(ctxt, ctxt->node);
2798     xsltFreeStackElemList(varsPop(ctxt));
2799
2800     xsltCleanupTemplates(style); /* TODO: <- style should be read only */
2801
2802     /*
2803      * Now cleanup our variables so stylesheet can be re-used
2804      *
2805      * TODO: this is not needed anymore global variables are copied
2806      *       and not evaluated directly anymore, keep this as a check
2807      */
2808     if (style->variables != variables) {
2809         vptr = style->variables;
2810         while (vptr->next!=variables)
2811             vptr = vptr->next;
2812         vptr->next = NULL;
2813         xsltFreeStackElemList(style->variables);
2814         style->variables = variables;
2815     }
2816     vptr = style->variables;
2817     while(vptr!=NULL) {
2818         if (vptr->computed) {
2819             if (vptr->value != NULL) {
2820                 xmlXPathFreeObject(vptr->value);
2821                 vptr->value = NULL;
2822                 vptr->computed = 0;
2823             }
2824         }
2825         vptr = vptr->next;
2826     }
2827
2828
2829     /*
2830      * Do some post processing work depending on the generated output
2831      */
2832     root = xmlDocGetRootElement(res);
2833     if (root != NULL) {
2834         /*
2835          * Apply the default selection of the method
2836          */
2837         if ((method == NULL) &&
2838             (root->ns == NULL) &&
2839             (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
2840             xmlNodePtr tmp;
2841             tmp = res->children;
2842             while ((tmp != NULL) && (tmp != root)) {
2843                 if (tmp->type == XML_ELEMENT_NODE)
2844                     break;
2845                 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
2846                     break;
2847             }
2848             if (tmp == root) {
2849                 ctxt->type = XSLT_OUTPUT_HTML;
2850                 res->type = XML_HTML_DOCUMENT_NODE;
2851                 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
2852                     res->intSubset = xmlCreateIntSubset(res, root->name,
2853                                  doctypePublic, doctypeSystem);
2854 #ifdef XSLT_GENERATE_HTML_DOCTYPE
2855                 else {
2856                     if (version == NULL)
2857                         version = (const xmlChar *) "4.0";
2858                     xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
2859                     if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
2860                         res->intSubset = xmlCreateIntSubset(res, root->name,
2861                                      doctypePublic, doctypeSystem);
2862                 }
2863 #endif
2864             }
2865
2866         }
2867         if (ctxt->type == XSLT_OUTPUT_XML) {
2868             XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
2869             XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
2870             if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
2871                 res->intSubset = xmlCreateIntSubset(res, root->name,
2872                              doctypePublic, doctypeSystem);
2873         }
2874     }
2875     xmlXPathFreeNodeSet(ctxt->nodeList);
2876     xsltFreeTransformContext(ctxt);
2877     return(res);
2878
2879 error:
2880     if (res != NULL)
2881         xmlFreeDoc(res);
2882     if (ctxt != NULL)
2883         xsltFreeTransformContext(ctxt);
2884     return(NULL);
2885 }
2886