085f23630a27163b289dd630e5d92b43c31b4c07
[platform/upstream/libxslt.git] / libxslt / transform.c
1 /*
2  * transform.c: Implemetation of the XSL Transformation 1.0 engine
3  *            transform part, i.e. applying a Stylesheet to a document
4  *
5  * Reference:
6  *   http://www.w3.org/TR/1999/REC-xslt-19991116
7  *
8  * See Copyright for the status of this software.
9  *
10  * Daniel.Veillard@imag.fr
11  */
12
13 #include "xsltconfig.h"
14
15 #include <string.h>
16
17 #include <libxml/xmlmemory.h>
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20 #include <libxml/valid.h>
21 #include <libxml/hash.h>
22 #include <libxml/encoding.h>
23 #include <libxml/xmlerror.h>
24 #include <libxml/xpath.h>
25 #include <libxml/parserInternals.h>
26 #include <libxml/xpathInternals.h>
27 #include <libxml/HTMLtree.h>
28 #include "xslt.h"
29 #include "xsltInternals.h"
30 #include "xsltutils.h"
31 #include "pattern.h"
32 #include "transform.h"
33 #include "variables.h"
34 #include "numbersInternals.h"
35 #include "namespaces.h"
36 #include "attributes.h"
37 #include "templates.h"
38 #include "imports.h"
39 #include "keys.h"
40 #include "documents.h"
41
42 #define DEBUG_PROCESS
43
44 int xsltMaxDepth = 250;
45
46 /*
47  * Useful macros
48  */
49
50 #ifndef FALSE
51 # define FALSE (0 == 1)
52 # define TRUE (!FALSE)
53 #endif
54
55 #define IS_BLANK_NODE(n)                                                \
56     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
57
58 /*
59  * Generic function for accessing stacks in the transform Context
60  */
61
62 #define PUSH_AND_POP(scope, type, name)                                 \
63 scope int name##Push(xsltTransformContextPtr ctxt, type value) {        \
64     if (ctxt->name##Nr >= ctxt->name##Max) {                            \
65         ctxt->name##Max *= 2;                                           \
66         ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab,          \
67                      ctxt->name##Max * sizeof(ctxt->name##Tab[0]));     \
68         if (ctxt->name##Tab == NULL) {                                  \
69             xmlGenericError(xmlGenericErrorContext,                     \
70                     "realloc failed !\n");                              \
71             return(0);                                                  \
72         }                                                               \
73     }                                                                   \
74     ctxt->name##Tab[ctxt->name##Nr] = value;                            \
75     ctxt->name = value;                                                 \
76     return(ctxt->name##Nr++);                                           \
77 }                                                                       \
78 scope type name##Pop(xsltTransformContextPtr ctxt) {                    \
79     type ret;                                                           \
80     if (ctxt->name##Nr <= 0) return(0);                                 \
81     ctxt->name##Nr--;                                                   \
82     if (ctxt->name##Nr > 0)                                             \
83         ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1];               \
84     else                                                                \
85         ctxt->name = NULL;                                              \
86     ret = ctxt->name##Tab[ctxt->name##Nr];                              \
87     ctxt->name##Tab[ctxt->name##Nr] = 0;                                \
88     return(ret);                                                        \
89 }                                                                       \
90
91 /*
92  * Those macros actually generate the functions
93  */
94 PUSH_AND_POP(extern, xsltTemplatePtr, templ)
95 PUSH_AND_POP(extern, xsltStackElemPtr, vars)
96
97 /************************************************************************
98  *                                                                      *
99  *                      handling of transformation contexts             *
100  *                                                                      *
101  ************************************************************************/
102
103 /**
104  * xsltNewTransformContext:
105  * @style:  a parsed XSLT stylesheet
106  * @doc:  the input document
107  *
108  * Create a new XSLT TransformContext
109  *
110  * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
111  */
112 xsltTransformContextPtr
113 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
114     xsltTransformContextPtr cur;
115     xsltDocumentPtr docu;
116
117     cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
118     if (cur == NULL) {
119         xsltGenericError(xsltGenericErrorContext,
120                 "xsltNewTransformContext : malloc failed\n");
121         return(NULL);
122     }
123     memset(cur, 0, sizeof(xsltTransformContext));
124
125     /*
126      * initialize the template stack
127      */
128     cur->templTab = (xsltTemplatePtr *)
129                 xmlMalloc(10 * sizeof(xsltTemplatePtr));
130     if (cur->templTab == NULL) {
131         xmlGenericError(xmlGenericErrorContext,
132                 "xsltNewTransformContext: out of memory\n");
133         xmlFree(cur);
134         return(NULL);
135     }
136     cur->templNr = 0;
137     cur->templMax = 5;
138     cur->templ = NULL;
139
140     /*
141      * initialize the variables stack
142      */
143     cur->varsTab = (xsltStackElemPtr *)
144                 xmlMalloc(10 * sizeof(xsltStackElemPtr));
145     if (cur->varsTab == NULL) {
146         xmlGenericError(xmlGenericErrorContext,
147                 "xsltNewTransformContext: out of memory\n");
148         xmlFree(cur->templTab);
149         xmlFree(cur);
150         return(NULL);
151     }
152     cur->varsNr = 0;
153     cur->varsMax = 5;
154     cur->vars = NULL;
155
156     cur->style = style;
157     xmlXPathInit();
158     cur->xpathCtxt = xmlXPathNewContext(doc);
159     if (cur->xpathCtxt == NULL) {
160         xsltGenericError(xsltGenericErrorContext,
161                 "xsltNewTransformContext : xmlXPathNewContext failed\n");
162         xmlFree(cur->templTab);
163         xmlFree(cur->varsTab);
164         xmlFree(cur);
165         return(NULL);
166     }
167     XSLT_REGISTER_VARIABLE_LOOKUP(cur);
168     cur->xpathCtxt->nsHash = style->nsHash;
169     docu = xsltNewDocument(cur, doc);
170     if (docu == NULL) {
171         xsltGenericError(xsltGenericErrorContext,
172                 "xsltNewTransformContext : xsltNewDocument failed\n");
173         xmlFree(cur->templTab);
174         xmlFree(cur->varsTab);
175         xmlFree(cur);
176         return(NULL);
177     }
178     docu->main = 1;
179     cur->document = docu;
180     return(cur);
181 }
182
183 /**
184  * xsltFreeTransformContext:
185  * @ctxt:  an XSLT parser context
186  *
187  * Free up the memory allocated by @ctxt
188  */
189 void
190 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
191     if (ctxt == NULL)
192         return;
193     if (ctxt->xpathCtxt != NULL) {
194         ctxt->xpathCtxt->nsHash = NULL;
195         xmlXPathFreeContext(ctxt->xpathCtxt);
196     }
197     if (ctxt->templTab != NULL)
198         xmlFree(ctxt->templTab);
199     if (ctxt->varsTab != NULL)
200         xmlFree(ctxt->varsTab);
201     xsltFreeDocuments(ctxt);
202     memset(ctxt, -1, sizeof(xsltTransformContext));
203     xmlFree(ctxt);
204 }
205
206 /************************************************************************
207  *                                                                      *
208  *                      Copy of Nodes in an XSLT fashion                *
209  *                                                                      *
210  ************************************************************************/
211
212 xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
213                         xmlNodePtr insert);
214
215 /**
216  * xsltCopyNode:
217  * @ctxt:  a XSLT process context
218  * @node:  the element node in the source tree.
219  * @insert:  the parent in the result tree.
220  *
221  * Make a copy of the element node @node
222  * and insert it as last child of @insert
223  *
224  * Returns a pointer to the new node, or NULL in case of error
225  */
226 xmlNodePtr
227 xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
228              xmlNodePtr insert) {
229     xmlNodePtr copy;
230
231     copy = xmlCopyNode(node, 0);
232     copy->doc = ctxt->output;
233     if (copy != NULL) {
234         xmlAddChild(insert, copy);
235         if (node->type == XML_ELEMENT_NODE) {
236             /*
237              * Add namespaces as they are needed
238              */
239             if (node->nsDef != NULL)
240                 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
241         }
242         if (node->ns != NULL) {
243             copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
244         }
245     } else {
246         xsltGenericError(xsltGenericErrorContext,
247                 "xsltCopyNode: copy %s failed\n", node->name);
248     }
249     return(copy);
250 }
251
252 /**
253  * xsltCopyTreeList:
254  * @ctxt:  a XSLT process context
255  * @list:  the list of element node in the source tree.
256  * @insert:  the parent in the result tree.
257  *
258  * Make a copy of the full list of tree @list
259  * and insert them as last children of @insert
260  *
261  * Returns a pointer to the new list, or NULL in case of error
262  */
263 xmlNodePtr
264 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr list,
265              xmlNodePtr insert) {
266     xmlNodePtr copy, ret = NULL;
267
268     while (list != NULL) {
269         copy = xsltCopyTree(ctxt, list, insert);
270         if (ret != NULL)
271             ret = copy;
272         list = list->next;
273     }
274     return(ret);
275 }
276
277 /**
278  * xsltCopyTree:
279  * @ctxt:  a XSLT process context
280  * @node:  the element node in the source tree.
281  * @insert:  the parent in the result tree.
282  *
283  * Make a copy of the full tree under the element node @node
284  * and insert it as last child of @insert
285  *
286  * Returns a pointer to the new tree, or NULL in case of error
287  */
288 xmlNodePtr
289 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
290              xmlNodePtr insert) {
291     xmlNodePtr copy;
292
293     copy = xmlCopyNode(node, 0);
294     copy->doc = ctxt->output;
295     if (copy != NULL) {
296         xmlAddChild(insert, copy);
297         /*
298          * Add namespaces as they are needed
299          */
300         if (node->nsDef != NULL)
301             xsltCopyNamespaceList(ctxt, copy, node->nsDef);
302         if (node->ns != NULL) {
303             copy->ns = xsltGetNamespace(ctxt, node, node->ns, insert);
304         }
305         if (node->children != NULL)
306             copy->children = xsltCopyTreeList(ctxt, node->children, copy);
307     } else {
308         xsltGenericError(xsltGenericErrorContext,
309                 "xsltCopyTree: copy %s failed\n", node->name);
310     }
311     return(copy);
312 }
313 /************************************************************************
314  *                                                                      *
315  *                      
316  *                                                                      *
317  ************************************************************************/
318
319 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
320 void xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
321                  xmlNodePtr inst);
322 void xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst);
323 void xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst);
324
325 /**
326  * xsltSort:
327  * @ctxt:  a XSLT process context
328  * @node:  the node in the source tree.
329  * @inst:  the xslt sort node
330  *
331  * Process the xslt sort node on the source node
332  */
333 void
334 xsltSort(xsltTransformContextPtr ctxt, xmlNodePtr node,
335                    xmlNodePtr inst) {
336     xmlXPathObjectPtr *results = NULL;
337     xmlNodeSetPtr list = NULL;
338     xmlXPathParserContextPtr xpathParserCtxt = NULL;
339     xmlChar *prop = NULL;
340     xmlXPathObjectPtr res, tmp;
341     const xmlChar *start;
342     int descending = 0;
343     int number = 0;
344     int len = 0;
345     int i;
346     xmlNodePtr oldNode;
347
348     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
349         return;
350
351     list = ctxt->nodeList;
352     if ((list == NULL) || (list->nodeNr <= 1))
353         goto error; /* nothing to do */
354
355     len = list->nodeNr;
356
357     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"data-type");
358     if (prop != NULL) {
359         if (xmlStrEqual(prop, (const xmlChar *) "text"))
360             number = 0;
361         else if (xmlStrEqual(prop, (const xmlChar *) "number"))
362             number = 1;
363         else {
364             xsltGenericError(xsltGenericErrorContext,
365                  "xsltSort: no support for data-type = %s\n", prop);
366             goto error;
367         }
368         xmlFree(prop);
369     }
370     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"order");
371     if (prop != NULL) {
372         if (xmlStrEqual(prop, (const xmlChar *) "ascending"))
373             descending = 0;
374         else if (xmlStrEqual(prop, (const xmlChar *) "descending"))
375             descending = 1;
376         else {
377             xsltGenericError(xsltGenericErrorContext,
378                  "xsltSort: invalid value %s for order\n", prop);
379             goto error;
380         }
381         xmlFree(prop);
382     }
383     /* TODO: xsl:sort lang attribute */
384     /* TODO: xsl:sort case-order attribute */
385
386     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
387     if (prop == NULL) {
388         prop = xmlNodeGetContent(inst);
389         if (prop == NULL) {
390             xsltGenericError(xsltGenericErrorContext,
391                  "xsltSort: select is not defined\n");
392             return;
393         }
394     }
395
396     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
397     if (xpathParserCtxt == NULL)
398         goto error;
399     results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
400     if (results == NULL) {
401         xsltGenericError(xsltGenericErrorContext,
402              "xsltSort: memory allocation failure\n");
403         goto error;
404     }
405
406     start = xpathParserCtxt->cur;
407     oldNode = ctxt->node;
408     for (i = 0;i < len;i++) {
409         xpathParserCtxt->cur = start;
410         ctxt->xpathCtxt->contextSize = len;
411         ctxt->xpathCtxt->proximityPosition = i + 1;
412         ctxt->node = list->nodeTab[i];
413         ctxt->xpathCtxt->node = ctxt->node;
414         xmlXPathEvalExpr(xpathParserCtxt);
415         xmlXPathStringFunction(xpathParserCtxt, 1);
416         if (number)
417             xmlXPathNumberFunction(xpathParserCtxt, 1);
418         res = valuePop(xpathParserCtxt);
419         do {
420             tmp = valuePop(xpathParserCtxt);
421             if (tmp != NULL) {
422                 xmlXPathFreeObject(tmp);
423             }
424         } while (tmp != NULL);
425
426         if (res != NULL) {
427             if (number) {
428                 if (res->type == XPATH_NUMBER) {
429                     results[i] = res;
430                 } else {
431 #ifdef DEBUG_PROCESS
432                     xsltGenericDebug(xsltGenericDebugContext,
433                         "xsltSort: select didn't evaluate to a number\n");
434 #endif
435                     results[i] = NULL;
436                 }
437             } else {
438                 if (res->type == XPATH_STRING) {
439                     results[i] = res;
440                 } else {
441 #ifdef DEBUG_PROCESS
442                     xsltGenericDebug(xsltGenericDebugContext,
443                         "xsltSort: select didn't evaluate to a string\n");
444 #endif
445                     results[i] = NULL;
446                 }
447             }
448         }
449     }
450     ctxt->node = oldNode;
451
452     xsltSortFunction(list, &results[0], descending, number);
453
454 error:
455     if (xpathParserCtxt != NULL)
456         xmlXPathFreeParserContext(xpathParserCtxt);
457     if (prop != NULL)
458         xmlFree(prop);
459     if (results != NULL) {
460         for (i = 0;i < len;i++)
461             xmlXPathFreeObject(results[i]);
462         xmlFree(results);
463     }
464 }
465
466 /**
467  * xsltCopy:
468  * @ctxt:  a XSLT process context
469  * @node:  the node in the source tree.
470  * @inst:  the xslt copy node
471  *
472  * Process the xslt copy node on the source node
473  */
474 void
475 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
476                    xmlNodePtr inst) {
477     xmlChar *prop;
478     xmlNodePtr copy, oldInsert;
479
480     oldInsert = ctxt->insert;
481     if (ctxt->insert != NULL) {
482         switch (node->type) {
483             case XML_DOCUMENT_NODE:
484             case XML_HTML_DOCUMENT_NODE:
485                 break;
486             case XML_ELEMENT_NODE:
487 #ifdef DEBUG_PROCESS
488                 xsltGenericDebug(xsltGenericDebugContext,
489                                  "xsl:copy: node %s\n", node->name);
490 #endif
491                 copy = xsltCopyNode(ctxt, node, ctxt->insert);
492                 ctxt->insert = copy;
493                 prop = xmlGetNsProp(inst, (const xmlChar *)"use-attribute-sets",
494                                     XSLT_NAMESPACE);
495                 if (prop != NULL) {
496                     xsltApplyAttributeSet(ctxt, node, inst, prop);
497                     xmlFree(prop);
498                 }
499                 break;
500             case XML_ATTRIBUTE_NODE: {
501 #ifdef DEBUG_PROCESS
502                 xsltGenericDebug(xsltGenericDebugContext,
503                                  "xsl:copy: attribute %s\n", node->name);
504 #endif
505                 if (ctxt->insert->type == XML_ELEMENT_NODE) {
506                     xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur;
507                     if (attr->ns != NULL) {
508                         if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
509                             (xmlStrncasecmp(attr->ns->prefix,
510                                             (xmlChar *)"xml", 3))) {
511                             ret = xmlCopyProp(ctxt->insert, attr);
512                             ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
513                                                        ctxt->insert);
514                         } 
515                     } else
516                         ret = xmlCopyProp(ctxt->insert, attr);
517
518                     cur = ctxt->insert->properties;
519                     if (cur != NULL) {
520                         while (cur->next != NULL)
521                             cur = cur->next;
522                         cur->next = ret;
523                         ret->prev = cur;
524                     }else
525                         ctxt->insert->properties = ret;
526                 }
527                 break;
528             }
529             default:
530                 break;
531
532         }
533     }
534
535     switch (node->type) {
536         case XML_DOCUMENT_NODE:
537         case XML_HTML_DOCUMENT_NODE:
538         case XML_ELEMENT_NODE:
539             varsPush(ctxt, NULL);
540             xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
541             xsltFreeStackElemList(varsPop(ctxt));
542             break;
543         default:
544             break;
545     }
546     ctxt->insert = oldInsert;
547 }
548
549 /**
550  * xsltText:
551  * @ctxt:  a XSLT process context
552  * @node:  the node in the source tree.
553  * @inst:  the xslt text node
554  *
555  * Process the xslt text node on the source node
556  */
557 void
558 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node,
559             xmlNodePtr inst) {
560     xmlNodePtr copy;
561
562     if (inst->children != NULL) {
563         if ((inst->children->type != XML_TEXT_NODE) ||
564             (inst->children->next != NULL)) {
565             xsltGenericError(xsltGenericErrorContext,
566                  "xslt:text has content problem !\n");
567         } else {
568             xmlChar *prop;
569             xmlNodePtr text = inst->children;
570             
571             copy = xmlNewDocText(ctxt->output, text->content);
572             prop = xmlGetNsProp(inst,
573                     (const xmlChar *)"disable-output-escaping",
574                                 XSLT_NAMESPACE);
575             if (prop != NULL) {
576 #ifdef DEBUG_PARSING
577                 xsltGenericDebug(xsltGenericDebugContext,
578                      "Disable escaping: %s\n", text->content);
579 #endif
580                 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
581                     copy->name = xmlStringTextNoenc;
582                 } else if (!xmlStrEqual(prop,
583                                         (const xmlChar *)"no")){
584                     xsltGenericError(xsltGenericErrorContext,
585      "xslt:text: disable-output-escaping allow only yes or no\n");
586
587                 }
588                 xmlFree(prop);
589             }
590             xmlAddChild(ctxt->insert, copy);
591         }
592     }
593 }
594
595 /**
596  * xsltElement:
597  * @ctxt:  a XSLT process context
598  * @node:  the node in the source tree.
599  * @inst:  the xslt element node
600  *
601  * Process the xslt element node on the source node
602  */
603 void
604 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
605             xmlNodePtr inst) {
606     xmlChar *prop = NULL, *attributes = NULL;
607     xmlChar *ncname = NULL;
608     xmlChar *prefix = NULL;
609     xmlChar *value = NULL;
610     xmlNsPtr ns = NULL;
611     xmlNodePtr copy;
612     xmlNodePtr oldInsert;
613
614
615     if (ctxt->insert == NULL)
616         return;
617     /*
618      * stack and saves
619      */
620     oldInsert = ctxt->insert;
621
622     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
623     if (prop == NULL) {
624         xsltGenericError(xsltGenericErrorContext,
625              "xslt:element : name is missing\n");
626         goto error;
627     }
628
629     ncname = xmlSplitQName2(prop, &prefix);
630     if (ncname == NULL) {
631         ncname = prop;
632         prop = NULL;
633         prefix = NULL;
634     }
635     prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"namespace");
636     if (prop != NULL) {
637         ns = xsltGetSpecialNamespace(ctxt, inst, prop, prefix, ctxt->insert);
638     } else {
639         if (prefix != NULL) {
640             if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
641 #ifdef DEBUG_PARSING
642                 xsltGenericDebug(xsltGenericDebugContext,
643                      "xslt:element : xml prefix forbidden\n");
644 #endif
645                 goto error;
646             }
647             ns = xmlSearchNs(inst->doc, inst, prefix);
648             if (ns == NULL) {
649                 xsltGenericError(xsltGenericErrorContext,
650                     "no namespace bound to prefix %s\n", prefix);
651             } else {
652                 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
653             }
654         }
655     }
656
657     copy = xmlNewDocNode(ctxt->output, ns, ncname, NULL);
658     if (copy == NULL) {
659         xsltGenericError(xsltGenericErrorContext,
660             "xsl:element : creation of %s failed\n", ncname);
661         goto error;
662     }
663     xmlAddChild(ctxt->insert, copy);
664     ctxt->insert = copy;
665     attributes = xsltEvalAttrValueTemplate(ctxt, inst,
666                                        (const xmlChar *)"use-attribute-sets");
667     if (attributes != NULL) {
668         xsltApplyAttributeSet(ctxt, node, inst, attributes);
669         xmlFree(attributes);
670     }
671     
672     varsPush(ctxt, NULL);
673     xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
674     xsltFreeStackElemList(varsPop(ctxt));
675
676     ctxt->insert = oldInsert;
677
678 error:
679     if (prop != NULL)
680         xmlFree(prop);
681     if (ncname != NULL)
682         xmlFree(ncname);
683     if (prefix != NULL)
684         xmlFree(prefix);
685     if (value != NULL)
686         xmlFree(value);
687 }
688
689 /**
690  * xsltComment:
691  * @ctxt:  a XSLT process context
692  * @node:  the node in the source tree.
693  * @inst:  the xslt comment node
694  *
695  * Process the xslt comment node on the source node
696  */
697 void
698 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
699                    xmlNodePtr inst) {
700     xmlChar *value = NULL;
701     xmlNodePtr comment;
702
703     value = xsltEvalTemplateString(ctxt, node, inst);
704     /* TODO: check that there is no -- sequence and doesn't end up with - */
705 #ifdef DEBUG_PROCESS
706     if (value == NULL)
707         xsltGenericDebug(xsltGenericDebugContext,
708              "xsl:comment: empty\n");
709     else
710         xsltGenericDebug(xsltGenericDebugContext,
711              "xsl:comment: content %s\n", value);
712 #endif
713
714     comment = xmlNewComment(value);
715     xmlAddChild(ctxt->insert, comment);
716
717     if (value != NULL)
718         xmlFree(value);
719 }
720
721 /**
722  * xsltProcessingInstruction:
723  * @ctxt:  a XSLT process context
724  * @node:  the node in the source tree.
725  * @inst:  the xslt processing-instruction node
726  *
727  * Process the xslt processing-instruction node on the source node
728  */
729 void
730 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
731                    xmlNodePtr inst) {
732     xmlChar *ncname = NULL;
733     xmlChar *value = NULL;
734     xmlNodePtr pi;
735
736
737     if (ctxt->insert == NULL)
738         return;
739     ncname = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
740     if (ncname == NULL) {
741         xsltGenericError(xsltGenericErrorContext,
742              "xslt:processing-instruction : name is missing\n");
743         goto error;
744     }
745     /* TODO: check that it's both an an NCName and a PITarget. */
746
747
748     value = xsltEvalTemplateString(ctxt, node, inst);
749     /* TODO: check that there is no ?> sequence */
750 #ifdef DEBUG_PROCESS
751     if (value == NULL)
752         xsltGenericDebug(xsltGenericDebugContext,
753              "xsl:processing-instruction: %s empty\n", ncname);
754     else
755         xsltGenericDebug(xsltGenericDebugContext,
756              "xsl:processing-instruction: %s content %s\n", ncname, value);
757 #endif
758
759     pi = xmlNewPI(ncname, value);
760     xmlAddChild(ctxt->insert, pi);
761
762 error:
763     if (ncname != NULL)
764         xmlFree(ncname);
765     if (value != NULL)
766         xmlFree(value);
767 }
768
769 /**
770  * xsltCopyOf:
771  * @ctxt:  a XSLT process context
772  * @node:  the node in the source tree.
773  * @inst:  the xslt copy-of node
774  *
775  * Process the xslt copy-of node on the source node
776  */
777 void
778 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
779                    xmlNodePtr inst) {
780     xmlChar *prop = NULL;
781     xmlXPathObjectPtr res = NULL, tmp;
782     xmlXPathParserContextPtr xpathParserCtxt = NULL;
783     xmlNodePtr copy = NULL;
784     xmlNodeSetPtr list = NULL;
785     int i;
786
787     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
788         return;
789
790     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
791     if (prop == NULL) {
792         xsltGenericError(xsltGenericErrorContext,
793              "xslt:copy-of : select is missing\n");
794         goto error;
795     }
796 #ifdef DEBUG_PROCESS
797     xsltGenericDebug(xsltGenericDebugContext,
798          "xsltCopyOf: select %s\n", prop);
799 #endif
800
801     xpathParserCtxt =
802         xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
803     if (xpathParserCtxt == NULL)
804         goto error;
805     ctxt->xpathCtxt->node = node;
806     xmlXPathEvalExpr(xpathParserCtxt);
807     res = valuePop(xpathParserCtxt);
808     do {
809         tmp = valuePop(xpathParserCtxt);
810         if (tmp != NULL) {
811             xmlXPathFreeObject(tmp);
812         }
813     } while (tmp != NULL);
814     if (res != NULL) {
815         if ((res->type == XPATH_NODESET) || (res->type == XPATH_XSLT_TREE)) {
816             list = res->nodesetval;
817             if (list != NULL) {
818                 /* sort the list in document order */
819                 xsltDocumentSortFunction(list);
820                 /* append everything in this order under ctxt->insert */
821                 for (i = 0;i < list->nodeNr;i++) {
822                     if (list->nodeTab[i] == NULL)
823                         continue;
824                     if ((list->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
825                         (list->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) {
826                         xsltCopyTreeList(ctxt, list->nodeTab[i]->children,
827                                          ctxt->insert);
828                     } else {
829                         xsltCopyTree(ctxt, list->nodeTab[i], ctxt->insert);
830                     }
831                 }
832             }
833         } else {
834             /* convert to a string */
835             valuePush(xpathParserCtxt, res);
836             xmlXPathStringFunction(xpathParserCtxt, 1);
837             res = valuePop(xpathParserCtxt);
838             if ((res != NULL) && (res->type == XPATH_STRING)) {
839                 /* append content as text node */
840                 copy = xmlNewText(res->stringval);
841                 if (copy != NULL) {
842                     xmlAddChild(ctxt->insert, copy);
843                 }
844             }
845             do {
846                 tmp = valuePop(xpathParserCtxt);
847                 if (tmp != NULL) {
848                     xmlXPathFreeObject(tmp);
849                 }
850             } while (tmp != NULL);
851             if (copy == NULL) {
852                 xsltGenericError(xsltGenericErrorContext,
853                     "xsltDefaultProcessOneNode: text copy failed\n");
854             }
855 #ifdef DEBUG_PROCESS
856             else
857                 xsltGenericDebug(xsltGenericDebugContext,
858                      "xslcopyOf: result %s\n", res->stringval);
859 #endif
860         }
861     }
862
863 error:
864     if (xpathParserCtxt != NULL) {
865         xmlXPathFreeParserContext(xpathParserCtxt);
866         xpathParserCtxt = NULL;
867     }
868     if (prop != NULL)
869         xmlFree(prop);
870     if (res != NULL)
871         xmlXPathFreeObject(res);
872 }
873
874 /**
875  * xsltValueOf:
876  * @ctxt:  a XSLT process context
877  * @node:  the node in the source tree.
878  * @inst:  the xslt value-of node
879  *
880  * Process the xslt value-of node on the source node
881  */
882 void
883 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
884                    xmlNodePtr inst) {
885     xmlChar *prop;
886     int disableEscaping = 0;
887     xmlXPathObjectPtr res = NULL, tmp;
888     xmlXPathParserContextPtr xpathParserCtxt = NULL;
889     xmlNodePtr copy = NULL;
890
891     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
892         return;
893
894     prop = xmlGetNsProp(inst, (const xmlChar *)"disable-output-escaping",
895                         XSLT_NAMESPACE);
896     if (prop != NULL) {
897         if (xmlStrEqual(prop, (const xmlChar *)"yes"))
898             disableEscaping = 1;
899         else if (xmlStrEqual(prop, (const xmlChar *)"no"))
900             disableEscaping = 0;
901         else 
902             xsltGenericError(xsltGenericErrorContext,
903                  "invalud value %s for disable-output-escaping\n", prop);
904
905         xmlFree(prop);
906     }
907     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
908     if (prop == NULL) {
909         xsltGenericError(xsltGenericErrorContext,
910              "xsltValueOf: select is not defined\n");
911         return;
912     }
913 #ifdef DEBUG_PROCESS
914     xsltGenericDebug(xsltGenericDebugContext,
915          "xsltValueOf: select %s\n", prop);
916 #endif
917
918     xpathParserCtxt =
919         xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
920     if (xpathParserCtxt == NULL)
921         goto error;
922     ctxt->xpathCtxt->node = node;
923     xmlXPathEvalExpr(xpathParserCtxt);
924     xmlXPathStringFunction(xpathParserCtxt, 1);
925     res = valuePop(xpathParserCtxt);
926     do {
927         tmp = valuePop(xpathParserCtxt);
928         if (tmp != NULL) {
929             xmlXPathFreeObject(tmp);
930         }
931     } while (tmp != NULL);
932     if (res != NULL) {
933         if (res->type == XPATH_STRING) {
934             copy = xmlNewText(res->stringval);
935             if (copy != NULL) {
936                 if (disableEscaping)
937                     copy->name = xmlStringTextNoenc;
938                 xmlAddChild(ctxt->insert, copy);
939             }
940         }
941     }
942     if (copy == NULL) {
943         xsltGenericError(xsltGenericErrorContext,
944             "xsltDefaultProcessOneNode: text copy failed\n");
945     }
946 #ifdef DEBUG_PROCESS
947     else
948         xsltGenericDebug(xsltGenericDebugContext,
949              "xsltValueOf: result %s\n", res->stringval);
950 #endif
951 error:
952     if (xpathParserCtxt != NULL) {
953         xmlXPathFreeParserContext(xpathParserCtxt);
954         xpathParserCtxt = NULL;
955     }
956     if (prop != NULL)
957         xmlFree(prop);
958     if (res != NULL)
959         xmlXPathFreeObject(res);
960 }
961
962 /**
963  * xsltNumber:
964  * @ctxt:  a XSLT process context
965  * @node:  the node in the source tree.
966  * @cur:   the xslt number node
967  *
968  * Process the xslt number node on the source node
969  */
970 void
971 xsltNumber(xsltTransformContextPtr ctxt,
972            xmlNodePtr node,
973            xmlNodePtr cur)
974 {
975     xmlChar *prop;
976     xsltNumberData numdata;
977
978     if ((ctxt == NULL) || (cur == NULL))
979         return;
980
981     memset(&numdata, 0, sizeof(numdata));
982     
983     numdata.doc = cur->doc;
984     numdata.node = cur;
985     numdata.value = xmlGetNsProp(cur, (const xmlChar *)"value", XSLT_NAMESPACE);
986     
987     prop = xmlGetNsProp(cur, (const xmlChar *)"format", XSLT_NAMESPACE);
988     if (prop != NULL) {
989         numdata.format = prop;
990     } else {
991         numdata.format = xmlStrdup(BAD_CAST("1"));
992     }
993     
994     numdata.count = xmlGetNsProp(cur, (const xmlChar *)"count", XSLT_NAMESPACE);
995     numdata.from = xmlGetNsProp(cur, (const xmlChar *)"from", XSLT_NAMESPACE);
996     
997     prop = xmlGetNsProp(cur, (const xmlChar *)"level", XSLT_NAMESPACE);
998     if (prop != NULL) {
999         if (xmlStrEqual(prop, BAD_CAST("single")) ||
1000             xmlStrEqual(prop, BAD_CAST("multiple")) ||
1001             xmlStrEqual(prop, BAD_CAST("any"))) {
1002             numdata.level = prop;
1003         } else {
1004             xsltGenericError(xsltGenericErrorContext,
1005                              "invalid value %s for level\n", prop);
1006             xmlFree(prop);
1007         }
1008     }
1009     
1010     prop = xmlGetNsProp(cur, (const xmlChar *)"lang", XSLT_NAMESPACE);
1011     if (prop != NULL) {
1012         TODO;
1013         xmlFree(prop);
1014     }
1015     
1016     prop = xmlGetNsProp(cur, (const xmlChar *)"letter-value", XSLT_NAMESPACE);
1017     if (prop != NULL) {
1018         if (xmlStrEqual(prop, BAD_CAST("alphabetic"))) {
1019             TODO;
1020         } else if (xmlStrEqual(prop, BAD_CAST("traditional"))) {
1021             TODO;
1022         } else {
1023             xsltGenericError(xsltGenericErrorContext,
1024                              "invalid value %s for letter-value\n", prop);
1025         }
1026         xmlFree(prop);
1027     }
1028     
1029     prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", XSLT_NAMESPACE);
1030     if (prop != NULL) {
1031         numdata.groupingCharacter = prop[0];
1032         xmlFree(prop);
1033     }
1034     
1035     prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-size", XSLT_NAMESPACE);
1036     if (prop != NULL) {
1037         sscanf((char *)prop, "%d", &numdata.digitsPerGroup);
1038         xmlFree(prop);
1039     } else {
1040         numdata.groupingCharacter = 0;
1041     }
1042
1043     /* Set default values */
1044     if (numdata.value == NULL) {
1045         if (numdata.level == NULL) {
1046             numdata.level = xmlStrdup(BAD_CAST("single"));
1047         }
1048     }
1049     
1050     xsltNumberFormat(ctxt, &numdata, node);
1051
1052     if (numdata.level != NULL)
1053         xmlFree(numdata.level);
1054     if (numdata.count != NULL)
1055         xmlFree(numdata.count);
1056     if (numdata.from != NULL)
1057         xmlFree(numdata.from);
1058     if (numdata.value != NULL)
1059         xmlFree(numdata.value);
1060     if (numdata.format != NULL)
1061         xmlFree(numdata.format);
1062 }
1063
1064 /**
1065  * xsltDefaultProcessOneNode:
1066  * @ctxt:  a XSLT process context
1067  * @node:  the node in the source tree.
1068  *
1069  * Process the source node with the default built-in template rule:
1070  * <xsl:template match="*|/">
1071  *   <xsl:apply-templates/>
1072  * </xsl:template>
1073  *
1074  * and
1075  *
1076  * <xsl:template match="text()|@*">
1077  *   <xsl:value-of select="."/>
1078  * </xsl:template>
1079  *
1080  * Note also that namespaces declarations are copied directly:
1081  *
1082  * the built-in template rule is the only template rule that is applied
1083  * for namespace nodes.
1084  */
1085 void
1086 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1087     xmlNodePtr copy;
1088     xmlAttrPtr attrs;
1089     xmlNodePtr delete = NULL, cur;
1090     int strip_spaces = -1;
1091     int nbchild = 0, oldSize;
1092     int childno = 0, oldPos;
1093     xsltTemplatePtr template;
1094
1095     CHECK_STOPPED;
1096     /*
1097      * Handling of leaves
1098      */
1099     switch (node->type) {
1100         case XML_DOCUMENT_NODE:
1101         case XML_HTML_DOCUMENT_NODE:
1102         case XML_ELEMENT_NODE:
1103             break;
1104         case XML_CDATA_SECTION_NODE:
1105             template = xsltGetTemplate(ctxt, node, NULL);
1106             if (template) {
1107                 xmlNodePtr oldNode;
1108
1109 #ifdef DEBUG_PROCESS
1110                 xsltGenericDebug(xsltGenericDebugContext,
1111                  "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
1112                                  node->content);
1113 #endif
1114                 oldNode = ctxt->node;
1115                 ctxt->node = node;
1116                 templPush(ctxt, template);
1117                 varsPush(ctxt, NULL);
1118                 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1119                 xsltFreeStackElemList(varsPop(ctxt));
1120                 templPop(ctxt);
1121                 ctxt->node = oldNode;
1122             } else /* if (ctxt->mode == NULL) */ {
1123 #ifdef DEBUG_PROCESS
1124                 xsltGenericDebug(xsltGenericDebugContext,
1125                  "xsltDefaultProcessOneNode: copy CDATA %s\n",
1126                                  node->content);
1127 #endif
1128                 copy = xmlNewDocText(ctxt->output, node->content);
1129                 if (copy != NULL) {
1130                     xmlAddChild(ctxt->insert, copy);
1131                 } else {
1132                     xsltGenericError(xsltGenericErrorContext,
1133                         "xsltDefaultProcessOneNode: cdata copy failed\n");
1134                 }
1135             }
1136             return;
1137         case XML_TEXT_NODE:
1138             template = xsltGetTemplate(ctxt, node, NULL);
1139             if (template) {
1140                 xmlNodePtr oldNode;
1141
1142 #ifdef DEBUG_PROCESS
1143                 xsltGenericDebug(xsltGenericDebugContext,
1144                  "xsltDefaultProcessOneNode: applying template for text %s\n",
1145                                  node->content);
1146 #endif
1147                 oldNode = ctxt->node;
1148                 ctxt->node = node;
1149                 templPush(ctxt, template);
1150                 varsPush(ctxt, NULL);
1151                 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1152                 xsltFreeStackElemList(varsPop(ctxt));
1153                 templPop(ctxt);
1154                 ctxt->node = oldNode;
1155             } else /* if (ctxt->mode == NULL) */ {
1156 #ifdef DEBUG_PROCESS
1157                 if (node->content == NULL)
1158                     xsltGenericDebug(xsltGenericDebugContext,
1159                      "xsltDefaultProcessOneNode: copy empty text\n");
1160                 else
1161                     xsltGenericDebug(xsltGenericDebugContext,
1162                      "xsltDefaultProcessOneNode: copy text %s\n",
1163                                      node->content);
1164 #endif
1165                 copy = xmlCopyNode(node, 0);
1166                 if (copy != NULL) {
1167                     xmlAddChild(ctxt->insert, copy);
1168                 } else {
1169                     xsltGenericError(xsltGenericErrorContext,
1170                         "xsltDefaultProcessOneNode: text copy failed\n");
1171                 }
1172             }
1173             return;
1174         case XML_ATTRIBUTE_NODE:
1175             if (ctxt->insert->type == XML_ELEMENT_NODE) {
1176                     xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur;
1177                 template = xsltGetTemplate(ctxt, node, NULL);
1178                 if (template) {
1179                     xmlNodePtr oldNode;
1180
1181                     oldNode = ctxt->node;
1182                     ctxt->node = node;
1183                     templPush(ctxt, template);
1184                     varsPush(ctxt, NULL);
1185                     xsltApplyOneTemplate(ctxt, node, template->content, 1);
1186                     xsltFreeStackElemList(varsPop(ctxt));
1187                     templPop(ctxt);
1188                     ctxt->node = oldNode;
1189                 } else if (ctxt->mode == NULL) {
1190                     if (attr->ns != NULL) {
1191                         if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
1192                             (xmlStrncasecmp(attr->ns->prefix,
1193                                             (xmlChar *)"xml", 3))) {
1194                             ret = xmlCopyProp(ctxt->insert, attr);
1195                             ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
1196                                                        ctxt->insert);
1197                         } 
1198                     } else
1199                         ret = xmlCopyProp(ctxt->insert, attr);
1200
1201                     cur = ctxt->insert->properties;
1202                     if (cur != NULL) {
1203                         while (cur->next != NULL)
1204                             cur = cur->next;
1205                         cur->next = ret;
1206                         ret->prev = cur;
1207                     }else
1208                         ctxt->insert->properties = ret;
1209                 }
1210             }
1211             return;
1212         default:
1213             return;
1214     }
1215     /*
1216      * Handling of Elements: first pass, cleanup and counting
1217      */
1218     cur = node->children;
1219     while (cur != NULL) {
1220         switch (cur->type) {
1221             case XML_TEXT_NODE:
1222                 if ((IS_BLANK_NODE(cur)) &&
1223                     (cur->parent != NULL) &&
1224                     (ctxt->style->stripSpaces != NULL)) {
1225                     if (strip_spaces == -1)
1226                         strip_spaces =
1227                             xsltFindElemSpaceHandling(ctxt, cur->parent);
1228                     if (strip_spaces == 1) {
1229                         delete = cur;
1230                         break;
1231                     }
1232                 }
1233                 /* no break on purpose */
1234             case XML_CDATA_SECTION_NODE:
1235             case XML_DOCUMENT_NODE:
1236             case XML_HTML_DOCUMENT_NODE:
1237             case XML_ELEMENT_NODE:
1238                 nbchild++;
1239                 break;
1240             case XML_PI_NODE:
1241             case XML_COMMENT_NODE:
1242                 nbchild++;
1243                 break;
1244             default:
1245 #ifdef DEBUG_PROCESS
1246                 xsltGenericDebug(xsltGenericDebugContext,
1247                  "xsltDefaultProcessOneNode: skipping node type %d\n",
1248                                  cur->type);
1249 #endif
1250                 delete = cur;
1251         }
1252         cur = cur->next;
1253         if (delete != NULL) {
1254 #ifdef DEBUG_PROCESS
1255             xsltGenericDebug(xsltGenericDebugContext,
1256                  "xsltDefaultProcessOneNode: removing ignorable blank node\n");
1257 #endif
1258             xmlUnlinkNode(delete);
1259             xmlFreeNode(delete);
1260             delete = NULL;
1261         }
1262     }
1263     /*
1264      * Handling of Elements: second pass, actual processing
1265      */
1266     attrs = node->properties;
1267     while (attrs != NULL) {
1268         template = xsltGetTemplate(ctxt, (xmlNodePtr) attrs, NULL);
1269         if (template) {
1270             xmlNodePtr oldNode;
1271
1272             oldNode = ctxt->node;
1273             ctxt->node = node;
1274             templPush(ctxt, template);
1275             varsPush(ctxt, NULL);
1276             xsltApplyOneTemplate(ctxt, node, template->content, 1);
1277             xsltFreeStackElemList(varsPop(ctxt));
1278             templPop(ctxt);
1279             ctxt->node = oldNode;
1280         }
1281         attrs = attrs->next;
1282     }
1283     oldSize = ctxt->xpathCtxt->contextSize;
1284     oldPos = ctxt->xpathCtxt->proximityPosition;
1285     cur = node->children;
1286     while (cur != NULL) {
1287         childno++;
1288         switch (cur->type) {
1289             case XML_DOCUMENT_NODE:
1290             case XML_HTML_DOCUMENT_NODE:
1291             case XML_ELEMENT_NODE:
1292                 ctxt->xpathCtxt->contextSize = nbchild;
1293                 ctxt->xpathCtxt->proximityPosition = childno;
1294                 xsltProcessOneNode(ctxt, cur);
1295                 break;
1296             case XML_CDATA_SECTION_NODE:
1297                 template = xsltGetTemplate(ctxt, node, NULL);
1298                 if (template) {
1299                     xmlNodePtr oldNode;
1300
1301 #ifdef DEBUG_PROCESS
1302                     xsltGenericDebug(xsltGenericDebugContext,
1303                  "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
1304                                      node->content);
1305 #endif
1306                     oldNode = ctxt->node;
1307                     ctxt->node = node;
1308                     templPush(ctxt, template);
1309                     varsPush(ctxt, NULL);
1310                     xsltApplyOneTemplate(ctxt, node, template->content, 1);
1311                     xsltFreeStackElemList(varsPop(ctxt));
1312                     templPop(ctxt);
1313                     ctxt->node = oldNode;
1314                 } else /* if (ctxt->mode == NULL) */ {
1315 #ifdef DEBUG_PROCESS
1316                     xsltGenericDebug(xsltGenericDebugContext,
1317                      "xsltDefaultProcessOneNode: copy CDATA %s\n",
1318                                      node->content);
1319 #endif
1320                     copy = xmlNewDocText(ctxt->output, node->content);
1321                     if (copy != NULL) {
1322                         xmlAddChild(ctxt->insert, copy);
1323                     } else {
1324                         xsltGenericError(xsltGenericErrorContext,
1325                             "xsltDefaultProcessOneNode: cdata copy failed\n");
1326                     }
1327                 }
1328                 break;
1329             case XML_TEXT_NODE:
1330                 template = xsltGetTemplate(ctxt, cur, NULL);
1331                 if (template) {
1332                     xmlNodePtr oldNode;
1333
1334 #ifdef DEBUG_PROCESS
1335                     xsltGenericDebug(xsltGenericDebugContext,
1336              "xsltDefaultProcessOneNode: applying template for text %s\n",
1337                                      node->content);
1338 #endif
1339                     oldNode = ctxt->node;
1340                     ctxt->node = cur;
1341                     ctxt->xpathCtxt->contextSize = nbchild;
1342                     ctxt->xpathCtxt->proximityPosition = childno;
1343                     templPush(ctxt, template);
1344                     varsPush(ctxt, NULL);
1345                     xsltApplyOneTemplate(ctxt, cur, template->content, 1);
1346                     xsltFreeStackElemList(varsPop(ctxt));
1347                     templPop(ctxt);
1348                     ctxt->node = oldNode;
1349                 } else /* if (ctxt->mode == NULL) */ {
1350 #ifdef DEBUG_PROCESS
1351                     if (cur->content == NULL)
1352                         xsltGenericDebug(xsltGenericDebugContext,
1353                          "xsltDefaultProcessOneNode: copy empty text\n");
1354                     else
1355                         xsltGenericDebug(xsltGenericDebugContext,
1356                      "xsltDefaultProcessOneNode: copy text %s\n",
1357                                          cur->content);
1358 #endif
1359                     copy = xmlCopyNode(cur, 0);
1360                     if (copy != NULL) {
1361                         xmlAddChild(ctxt->insert, copy);
1362                     } else {
1363                         xsltGenericError(xsltGenericErrorContext,
1364                             "xsltDefaultProcessOneNode: text copy failed\n");
1365                     }
1366                 }
1367                 break;
1368             case XML_PI_NODE:
1369             case XML_COMMENT_NODE:
1370                 template = xsltGetTemplate(ctxt, cur, NULL);
1371                 if (template) {
1372                     xmlNodePtr oldNode;
1373
1374                     oldNode = ctxt->node;
1375                     ctxt->node = cur;
1376                     ctxt->xpathCtxt->contextSize = nbchild;
1377                     ctxt->xpathCtxt->proximityPosition = childno;
1378                     templPush(ctxt, template);
1379                     varsPush(ctxt, NULL);
1380                     xsltApplyOneTemplate(ctxt, cur, template->content, 1);
1381                     xsltFreeStackElemList(varsPop(ctxt));
1382                     templPop(ctxt);
1383                     ctxt->node = oldNode;
1384                 }
1385                 break;
1386             default:
1387                 break;
1388         }
1389         cur = cur->next;
1390     }
1391     ctxt->xpathCtxt->contextSize = oldSize;
1392     ctxt->xpathCtxt->proximityPosition = oldPos;
1393 }
1394
1395 /**
1396  * xsltApplyImports:
1397  * @ctxt:  a XSLT process context
1398  * @node:  the node in the source tree.
1399  * @inst:  the xslt apply-imports node
1400  *
1401  * Process the xslt apply-imports node on the source node
1402  */
1403 void
1404 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr node,
1405                  xmlNodePtr inst) {
1406     xsltTemplatePtr template;
1407
1408     if ((ctxt->templ == NULL) || (ctxt->templ->style == NULL)) {
1409         xsltGenericError(xsltGenericErrorContext,
1410              "xslt:apply-imports : internal error no current template\n");
1411         return;
1412     }
1413     template = xsltGetTemplate(ctxt, node, ctxt->templ->style);
1414     if (template != NULL) {
1415         templPush(ctxt, template);
1416         varsPush(ctxt, NULL);
1417         xsltApplyOneTemplate(ctxt, node, template->content, 1);
1418         xsltFreeStackElemList(varsPop(ctxt));
1419         templPop(ctxt);
1420     }
1421 }
1422
1423 /**
1424  * xsltCallTemplate:
1425  * @ctxt:  a XSLT process context
1426  * @node:  the node in the source tree.
1427  * @inst:  the xslt call-template node
1428  *
1429  * Process the xslt call-template node on the source node
1430  */
1431 void
1432 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
1433                    xmlNodePtr inst) {
1434     xmlChar *prop = NULL;
1435     xmlChar *ncname = NULL;
1436     xmlChar *prefix = NULL;
1437     xmlNsPtr ns = NULL;
1438     xsltTemplatePtr template;
1439     xmlNodePtr cur = NULL;
1440
1441
1442     if (ctxt->insert == NULL)
1443         return;
1444     prop = xmlGetNsProp(inst, (const xmlChar *)"name", XSLT_NAMESPACE);
1445     if (prop == NULL) {
1446         xsltGenericError(xsltGenericErrorContext,
1447              "xslt:call-template : name is missing\n");
1448         goto error;
1449     }
1450
1451     ncname = xmlSplitQName2(prop, &prefix);
1452     if (ncname == NULL) {
1453         ncname = prop;
1454         prop = NULL;
1455         prefix = NULL;
1456     }
1457     if ((prefix != NULL) && (ns == NULL)) {
1458         ns = xmlSearchNs(ctxt->insert->doc, ctxt->insert, prefix);
1459         if (ns == NULL) {
1460             xsltGenericError(xsltGenericErrorContext,
1461                 "no namespace bound to prefix %s\n", prefix);
1462         }
1463     }
1464     if (ns != NULL)
1465         template = xsltFindTemplate(ctxt, ncname, ns->href);
1466     else
1467         template = xsltFindTemplate(ctxt, ncname, NULL);
1468     if (template == NULL) {
1469         xsltGenericError(xsltGenericDebugContext,
1470              "xslt:call-template: template %s not found\n", ncname);
1471         goto error;
1472     }
1473     templPush(ctxt, template);
1474     varsPush(ctxt, NULL);
1475     cur = inst->children;
1476     while (cur != NULL) {
1477         if (ctxt->state == XSLT_STATE_STOPPED) break;
1478         if (IS_XSLT_ELEM(cur)) {
1479             if (IS_XSLT_NAME(cur, "with-param")) {
1480                 xsltParseStylesheetParam(ctxt, cur);
1481             } else {
1482                 xsltGenericError(xsltGenericDebugContext,
1483                      "xslt:call-template: misplaced xslt:%s\n", cur->name);
1484             }
1485         } else {
1486             xsltGenericError(xsltGenericDebugContext,
1487                  "xslt:call-template: misplaced %s element\n", cur->name);
1488         }
1489         cur = cur->next;
1490     }
1491     xsltApplyOneTemplate(ctxt, node, template->content, 1);
1492
1493     xsltFreeStackElemList(varsPop(ctxt));
1494     templPop(ctxt);
1495
1496 error:
1497     if (prop != NULL)
1498         xmlFree(prop);
1499     if (ncname != NULL)
1500         xmlFree(ncname);
1501     if (prefix != NULL)
1502         xmlFree(prefix);
1503 }
1504
1505 /**
1506  * xsltApplyTemplates:
1507  * @ctxt:  a XSLT process context
1508  * @node:  the node in the source tree.
1509  * @inst:  the apply-templates node
1510  *
1511  * Process the apply-templates node on the source node
1512  */
1513 void
1514 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
1515                    xmlNodePtr inst) {
1516     xmlChar *prop = NULL;
1517     xmlNodePtr cur, delete = NULL;
1518     xmlXPathObjectPtr res = NULL, tmp;
1519     xmlNodePtr replacement;
1520     xmlNodeSetPtr list = NULL, oldlist;
1521     xmlXPathParserContextPtr xpathParserCtxt = NULL;
1522     int i, oldProximityPosition, oldContextSize;
1523     xmlChar *mode, *modeURI;
1524     const xmlChar *oldmode, *oldmodeURI;
1525
1526     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1527         return;
1528
1529 #ifdef DEBUG_PROCESS
1530     xsltGenericDebug(xsltGenericDebugContext,
1531          "xsltApplyTemplates: node: %s\n", node->name);
1532 #endif
1533
1534     /*
1535      * Get mode if any
1536      */
1537     oldmode = ctxt->mode;
1538     oldmodeURI = ctxt->modeURI;
1539     prop = xmlGetNsProp(inst, (const xmlChar *)"mode", XSLT_NAMESPACE);
1540     if (prop != NULL) {
1541         xmlChar *prefix = NULL;
1542
1543         mode = xmlSplitQName2(prop, &prefix);
1544         if (mode != NULL) {
1545             if (prefix != NULL) {
1546                 xmlNsPtr ns;
1547
1548                 ns = xmlSearchNs(inst->doc, inst, prefix);
1549                 if (ns == NULL) {
1550                     xsltGenericError(xsltGenericErrorContext,
1551                         "no namespace bound to prefix %s\n", prefix);
1552                     xmlFree(prefix);
1553                     xmlFree(mode);
1554                     mode = prop;
1555                     modeURI = NULL;
1556                 } else {
1557                     modeURI = xmlStrdup(ns->href);
1558                     xmlFree(prefix);
1559                     xmlFree(prop);
1560                 }
1561             } else {
1562                 xmlFree(prop);
1563                 modeURI = NULL;
1564             }
1565         } else {
1566             mode = prop;
1567             modeURI = NULL;
1568         }
1569 #ifdef DEBUG_PROCESS
1570         xsltGenericDebug(xsltGenericDebugContext,
1571              "xsltApplyTemplates: mode %s\n", mode);
1572 #endif
1573     } else {
1574         mode = NULL;
1575         modeURI = NULL;
1576     }
1577     ctxt->mode = mode;
1578     ctxt->modeURI = modeURI;
1579
1580     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
1581     if (prop != NULL) {
1582 #ifdef DEBUG_PROCESS
1583         xsltGenericDebug(xsltGenericDebugContext,
1584              "xsltApplyTemplates: select %s\n", prop);
1585 #endif
1586
1587         if (ctxt->xpathCtxt == NULL) {
1588         }
1589         xpathParserCtxt =
1590             xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1591         if (xpathParserCtxt == NULL)
1592             goto error;
1593         ctxt->xpathCtxt->node = node;
1594         xmlXPathEvalExpr(xpathParserCtxt);
1595         res = valuePop(xpathParserCtxt);
1596         do {
1597             tmp = valuePop(xpathParserCtxt);
1598             if (tmp != NULL) {
1599                 xmlXPathFreeObject(tmp);
1600             }
1601         } while (tmp != NULL);
1602         if (res != NULL) {
1603             if (res->type == XPATH_NODESET) {
1604                 list = res->nodesetval;
1605                 res->nodesetval = NULL;
1606              } else {
1607 #ifdef DEBUG_PROCESS
1608                 xsltGenericDebug(xsltGenericDebugContext,
1609                     "xsltApplyTemplates: select didn't evaluate to a node list\n");
1610 #endif
1611                 goto error;
1612             }
1613         }
1614     } else {
1615         /*
1616          * Build an XPath nodelist with the children
1617          */
1618         list = xmlXPathNodeSetCreate(NULL);
1619         cur = node->children;
1620         while (cur != NULL) {
1621             switch (cur->type) {
1622                 case XML_TEXT_NODE:
1623                     if ((IS_BLANK_NODE(cur)) &&
1624                         (cur->parent != NULL) &&
1625                         (ctxt->style->stripSpaces != NULL)) {
1626                         const xmlChar *val;
1627
1628                         val = (const xmlChar *)
1629                               xmlHashLookup(ctxt->style->stripSpaces,
1630                                             cur->parent->name);
1631                         if ((val != NULL) &&
1632                             (xmlStrEqual(val, (xmlChar *) "strip"))) {
1633                             delete = cur;
1634                             break;
1635                         }
1636                     }
1637                     /* no break on purpose */
1638                 case XML_DOCUMENT_NODE:
1639                 case XML_HTML_DOCUMENT_NODE:
1640                 case XML_ELEMENT_NODE:
1641                 case XML_CDATA_SECTION_NODE:
1642                     xmlXPathNodeSetAdd(list, cur);
1643                     break;
1644                 default:
1645 #ifdef DEBUG_PROCESS
1646                     xsltGenericDebug(xsltGenericDebugContext,
1647                      "xsltApplyTemplates: skipping cur type %d\n",
1648                                      cur->type);
1649 #endif
1650                     delete = cur;
1651             }
1652             cur = cur->next;
1653             if (delete != NULL) {
1654 #ifdef DEBUG_PROCESS
1655                 xsltGenericDebug(xsltGenericDebugContext,
1656                      "xsltApplyTemplates: removing ignorable blank cur\n");
1657 #endif
1658                 xmlUnlinkNode(delete);
1659                 xmlFreeNode(delete);
1660                 delete = NULL;
1661             }
1662         }
1663     }
1664
1665 #ifdef DEBUG_PROCESS
1666     xsltGenericDebug(xsltGenericDebugContext,
1667         "xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
1668 #endif
1669
1670     oldlist = ctxt->nodeList;
1671     ctxt->nodeList = list;
1672     oldContextSize = ctxt->xpathCtxt->contextSize;
1673     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1674     ctxt->xpathCtxt->contextSize = list->nodeNr;
1675
1676     /* 
1677      * handle and skip the xsl:sort
1678      */
1679     replacement = inst->children;
1680     if (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
1681         xsltSort(ctxt, node, replacement);
1682         replacement = replacement->next;
1683         while (IS_XSLT_ELEM(replacement) &&
1684                (IS_XSLT_NAME(replacement, "sort"))) {
1685             TODO /* imbricated sorts */
1686             replacement = replacement->next;
1687         }
1688     }
1689
1690     for (i = 0;i < list->nodeNr;i++) {
1691         ctxt->node = list->nodeTab[i];
1692         ctxt->xpathCtxt->proximityPosition = i + 1;
1693         xsltProcessOneNode(ctxt, list->nodeTab[i]);
1694     }
1695     ctxt->nodeList = oldlist;
1696     ctxt->xpathCtxt->contextSize = oldContextSize;
1697     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1698
1699 error:
1700     ctxt->mode = oldmode;
1701     ctxt->modeURI = oldmodeURI;
1702     if (xpathParserCtxt != NULL)
1703         xmlXPathFreeParserContext(xpathParserCtxt);
1704     if (prop != NULL)
1705         xmlFree(prop);
1706     if (res != NULL)
1707         xmlXPathFreeObject(res);
1708     if (list != NULL)
1709         xmlXPathFreeNodeSet(list);
1710     if (mode != NULL)
1711         xmlFree(mode);
1712     if (modeURI != NULL)
1713         xmlFree(modeURI);
1714 }
1715
1716
1717 /**
1718  * xsltApplyOneTemplate:
1719  * @ctxt:  a XSLT process context
1720  * @node:  the node in the source tree.
1721  * @list:  the template replacement nodelist
1722  * @real: is this a real template processing
1723  *
1724  * Process the apply-templates node on the source node
1725  */
1726 void
1727 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
1728                      xmlNodePtr list, int real) {
1729     xmlNodePtr cur = NULL, insert, copy = NULL;
1730     xmlNodePtr oldInsert;
1731     xmlNodePtr oldCurrent;
1732     xmlAttrPtr attrs;
1733
1734     if (list == NULL)
1735         return;
1736     CHECK_STOPPED;
1737
1738     if (ctxt->templNr >= xsltMaxDepth) {
1739         xsltGenericError(xsltGenericErrorContext,
1740                 "xsltApplyOneTemplate: loop found ???\n");
1741         xsltGenericError(xsltGenericErrorContext,
1742                 "try increasing xsltMaxDepth (--maxdepth)\n");
1743         xsltDebug(ctxt, node);
1744         return;
1745     }
1746
1747     /*
1748      * stack and saves
1749      */
1750     oldInsert = insert = ctxt->insert;
1751     if (real) {
1752         oldCurrent = ctxt->current;
1753         ctxt->current = node;
1754     }
1755
1756     /*
1757      * Insert all non-XSLT nodes found in the template
1758      */
1759     cur = list;
1760     while (cur != NULL) {
1761         /*
1762          * test, we must have a valid insertion point
1763          */
1764         if (insert == NULL) {
1765 #ifdef DEBUG_PROCESS
1766             xsltGenericDebug(xsltGenericDebugContext,
1767                  "xsltApplyOneTemplate: insert == NULL !\n");
1768 #endif
1769             if (real)
1770                 ctxt->current = oldCurrent;
1771             return;
1772         }
1773
1774         if (IS_XSLT_ELEM(cur)) {
1775             if (IS_XSLT_NAME(cur, "apply-templates")) {
1776                 ctxt->insert = insert;
1777                 xsltApplyTemplates(ctxt, node, cur);
1778                 ctxt->insert = oldInsert;
1779             } else if (IS_XSLT_NAME(cur, "value-of")) {
1780                 ctxt->insert = insert;
1781                 xsltValueOf(ctxt, node, cur);
1782                 ctxt->insert = oldInsert;
1783             } else if (IS_XSLT_NAME(cur, "copy")) {
1784                 ctxt->insert = insert;
1785                 xsltCopy(ctxt, node, cur);
1786                 ctxt->insert = oldInsert;
1787             } else if (IS_XSLT_NAME(cur, "copy-of")) {
1788                 ctxt->insert = insert;
1789                 xsltCopyOf(ctxt, node, cur);
1790                 ctxt->insert = oldInsert;
1791             } else if (IS_XSLT_NAME(cur, "if")) {
1792                 ctxt->insert = insert;
1793                 xsltIf(ctxt, node, cur);
1794                 ctxt->insert = oldInsert;
1795             } else if (IS_XSLT_NAME(cur, "choose")) {
1796                 ctxt->insert = insert;
1797                 xsltChoose(ctxt, node, cur);
1798                 ctxt->insert = oldInsert;
1799             } else if (IS_XSLT_NAME(cur, "for-each")) {
1800                 ctxt->insert = insert;
1801                 xsltForEach(ctxt, node, cur);
1802                 ctxt->insert = oldInsert;
1803             } else if (IS_XSLT_NAME(cur, "apply-imports")) {
1804                 ctxt->insert = insert;
1805                 xsltApplyImports(ctxt, node, cur);
1806                 ctxt->insert = oldInsert;
1807             } else if (IS_XSLT_NAME(cur, "attribute")) {
1808                 ctxt->insert = insert;
1809                 xsltAttribute(ctxt, node, cur);
1810                 ctxt->insert = oldInsert;
1811             } else if (IS_XSLT_NAME(cur, "element")) {
1812                 ctxt->insert = insert;
1813                 xsltElement(ctxt, node, cur);
1814                 ctxt->insert = oldInsert;
1815             } else if (IS_XSLT_NAME(cur, "text")) {
1816 #ifdef DEBUG_PROCESS
1817                 xsltGenericDebug(xsltGenericDebugContext,
1818                      "xsltApplyOneTemplate: found xslt:text, not expected\n");
1819 #endif
1820                 xsltText(ctxt, node, cur);
1821             } else if (IS_XSLT_NAME(cur, "comment")) {
1822                 ctxt->insert = insert;
1823                 xsltComment(ctxt, node, cur);
1824                 ctxt->insert = oldInsert;
1825             } else if (IS_XSLT_NAME(cur, "number")) {
1826                 ctxt->insert = insert;
1827                 xsltNumber(ctxt, node, cur);
1828                 ctxt->insert = oldInsert;
1829             } else if (IS_XSLT_NAME(cur, "processing-instruction")) {
1830                 ctxt->insert = insert;
1831                 xsltProcessingInstruction(ctxt, node, cur);
1832                 ctxt->insert = oldInsert;
1833             } else if (IS_XSLT_NAME(cur, "variable")) {
1834                 xsltParseStylesheetVariable(ctxt, cur);
1835             } else if (IS_XSLT_NAME(cur, "param")) {
1836                 xsltParseStylesheetParam(ctxt, cur);
1837             } else if (IS_XSLT_NAME(cur, "call-template")) {
1838                 ctxt->insert = insert;
1839                 xsltCallTemplate(ctxt, node, cur);
1840                 ctxt->insert = oldInsert;
1841             } else if (IS_XSLT_NAME(cur, "message")) {
1842                 xsltMessage(ctxt, node, cur);
1843             } else {
1844                 xsltGenericError(xsltGenericDebugContext,
1845                      "xsltApplyOneTemplate: found xslt:%s\n", cur->name);
1846                 TODO
1847             }
1848             CHECK_STOPPED;
1849             goto skip_children;
1850         } else if (cur->type == XML_TEXT_NODE) {
1851             /*
1852              * This text comes from the stylesheet
1853              * For stylesheets, the set of whitespace-preserving
1854              * element names consists of just xsl:text.
1855              */
1856 #ifdef DEBUG_PROCESS
1857             if (cur->name == xmlStringTextNoenc)
1858                 xsltGenericDebug(xsltGenericDebugContext,
1859                      "xsltApplyOneTemplate: copy unescaped text %s\n",
1860                                  cur->content);
1861             else
1862                 xsltGenericDebug(xsltGenericDebugContext,
1863                      "xsltApplyOneTemplate: copy text %s\n", cur->content);
1864 #endif
1865             copy = xmlCopyNode(cur, 0);
1866             if (copy != NULL) {
1867                 xmlAddChild(insert, copy);
1868             } else {
1869                 xsltGenericError(xsltGenericErrorContext,
1870                         "xsltApplyOneTemplate: text copy failed\n");
1871             }
1872         } else if ((cur->type == XML_ELEMENT_NODE) &&
1873                    (xmlStrEqual(cur->name, (const xmlChar *)"xsltdebug"))) {
1874             xsltDebug(ctxt, cur);
1875         } else if (cur->type == XML_ELEMENT_NODE) {
1876 #ifdef DEBUG_PROCESS
1877             xsltGenericDebug(xsltGenericDebugContext,
1878                  "xsltApplyOneTemplate: copy node %s\n", cur->name);
1879 #endif
1880             copy = xsltCopyNode(ctxt, cur, insert);
1881             /*
1882              * all the attributes are directly inherited
1883              */
1884             if (cur->properties != NULL) {
1885                 attrs = xsltAttrListTemplateProcess(ctxt, copy,
1886                                                     cur->properties);
1887                 if (copy->properties != NULL) {
1888                     xmlAttrPtr cur = copy->properties;
1889                     while (cur->next != NULL)
1890                         cur = cur->next;
1891                     cur->next = attrs;
1892                 } else
1893                     copy->properties = attrs;
1894             }
1895         }
1896
1897         /*
1898          * Skip to next node, in document order.
1899          */
1900         if (cur->children != NULL) {
1901             if (cur->children->type != XML_ENTITY_DECL) {
1902                 cur = cur->children;
1903                 if (copy != NULL)
1904                     insert = copy;
1905                 continue;
1906             }
1907         }
1908 skip_children:
1909         if (cur->next != NULL) {
1910             cur = cur->next;
1911             continue;
1912         }
1913         
1914         do {
1915             cur = cur->parent;
1916             insert = insert->parent;
1917             if (cur == NULL)
1918                 break;
1919             if (cur == list->parent) {
1920                 cur = NULL;
1921                 break;
1922             }
1923             if (cur->next != NULL) {
1924                 cur = cur->next;
1925                 break;
1926             }
1927         } while (cur != NULL);
1928     }
1929     if (real)
1930         ctxt->current = oldCurrent;
1931 }
1932
1933 /**
1934  * xsltChoose:
1935  * @ctxt:  a XSLT process context
1936  * @node:  the node in the source tree.
1937  * @inst:  the xslt choose node
1938  *
1939  * Process the xslt choose node on the source node
1940  */
1941 void
1942 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node,
1943                    xmlNodePtr inst) {
1944     xmlChar *prop = NULL;
1945     xmlXPathObjectPtr res = NULL, tmp;
1946     xmlXPathParserContextPtr xpathParserCtxt = NULL;
1947     xmlNodePtr replacement, when;
1948     int doit = 1;
1949
1950     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1951         return;
1952
1953     /* 
1954      * Check the when's
1955      */
1956     replacement = inst->children;
1957     if (replacement == NULL) {
1958         xsltGenericError(xsltGenericErrorContext,
1959              "xslt:choose: empty content not allowed\n");
1960         goto error;
1961     }
1962     if ((!IS_XSLT_ELEM(replacement)) ||
1963         (!IS_XSLT_NAME(replacement, "when"))) {
1964         xsltGenericError(xsltGenericErrorContext,
1965              "xslt:choose: xsl:when expected first\n");
1966         goto error;
1967     }
1968     while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "when"))) {
1969
1970         when = replacement;
1971         prop = xmlGetNsProp(when, (const xmlChar *)"test", XSLT_NAMESPACE);
1972         if (prop == NULL) {
1973             xsltGenericError(xsltGenericErrorContext,
1974                  "xsl:when: test is not defined\n");
1975             return;
1976         }
1977 #ifdef DEBUG_PROCESS
1978         xsltGenericDebug(xsltGenericDebugContext,
1979              "xsl:when: test %s\n", prop);
1980 #endif
1981
1982         xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1983         if (xpathParserCtxt == NULL)
1984             goto error;
1985         ctxt->xpathCtxt->node = node;
1986         xmlXPathEvalExpr(xpathParserCtxt);
1987         xmlXPathBooleanFunction(xpathParserCtxt, 1);
1988         res = valuePop(xpathParserCtxt);
1989         do {
1990             tmp = valuePop(xpathParserCtxt);
1991             if (tmp != NULL) {
1992                 xmlXPathFreeObject(tmp);
1993             }
1994         } while (tmp != NULL);
1995
1996         if (res != NULL) {
1997             if (res->type == XPATH_BOOLEAN)
1998                 doit = res->boolval;
1999             else {
2000 #ifdef DEBUG_PROCESS
2001                 xsltGenericDebug(xsltGenericDebugContext,
2002                     "xsl:when: test didn't evaluate to a boolean\n");
2003 #endif
2004                 goto error;
2005             }
2006         }
2007
2008 #ifdef DEBUG_PROCESS
2009         xsltGenericDebug(xsltGenericDebugContext,
2010             "xsl:when: test evaluate to %d\n", doit);
2011 #endif
2012         if (doit) {
2013             varsPush(ctxt, NULL);
2014             xsltApplyOneTemplate(ctxt, ctxt->node, when->children, 0);
2015             xsltFreeStackElemList(varsPop(ctxt));
2016             goto done;
2017         }
2018         if (xpathParserCtxt != NULL)
2019             xmlXPathFreeParserContext(xpathParserCtxt);
2020         xpathParserCtxt = NULL;
2021         if (prop != NULL)
2022             xmlFree(prop);
2023         prop = NULL;
2024         if (res != NULL)
2025             xmlXPathFreeObject(res);
2026         res = NULL;
2027         replacement = replacement->next;
2028     }
2029     if (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "otherwise"))) {
2030         varsPush(ctxt, NULL);
2031         xsltApplyOneTemplate(ctxt, ctxt->node, replacement->children, 0);
2032         xsltFreeStackElemList(varsPop(ctxt));
2033         replacement = replacement->next;
2034     }
2035     if (replacement != NULL) {
2036 #ifdef DEBUG_PROCESS
2037         xsltGenericDebug(xsltGenericDebugContext,
2038             "xsl:otherwise: applying default fallback\n");
2039 #endif
2040         xsltGenericError(xsltGenericErrorContext,
2041              "xslt:choose: unexpected content %s\n", replacement->name);
2042         goto error;
2043     }
2044
2045 done:
2046 error:
2047     if (xpathParserCtxt != NULL)
2048         xmlXPathFreeParserContext(xpathParserCtxt);
2049     if (prop != NULL)
2050         xmlFree(prop);
2051     if (res != NULL)
2052         xmlXPathFreeObject(res);
2053 }
2054
2055 /**
2056  * xsltIf:
2057  * @ctxt:  a XSLT process context
2058  * @node:  the node in the source tree.
2059  * @inst:  the xslt if node
2060  *
2061  * Process the xslt if node on the source node
2062  */
2063 void
2064 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
2065                    xmlNodePtr inst) {
2066     xmlChar *prop;
2067     xmlXPathObjectPtr res = NULL, tmp;
2068     xmlXPathParserContextPtr xpathParserCtxt = NULL;
2069     int doit = 1;
2070
2071     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2072         return;
2073
2074     prop = xmlGetNsProp(inst, (const xmlChar *)"test", XSLT_NAMESPACE);
2075     if (prop == NULL) {
2076         xsltGenericError(xsltGenericErrorContext,
2077              "xsltIf: test is not defined\n");
2078         return;
2079     }
2080 #ifdef DEBUG_PROCESS
2081     xsltGenericDebug(xsltGenericDebugContext,
2082          "xsltIf: test %s\n", prop);
2083 #endif
2084
2085     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
2086     if (xpathParserCtxt == NULL)
2087         goto error;
2088     ctxt->xpathCtxt->node = node;
2089     xmlXPathEvalExpr(xpathParserCtxt);
2090     xmlXPathBooleanFunction(xpathParserCtxt, 1);
2091     res = valuePop(xpathParserCtxt);
2092     do {
2093         tmp = valuePop(xpathParserCtxt);
2094         if (tmp != NULL) {
2095             xmlXPathFreeObject(tmp);
2096         }
2097     } while (tmp != NULL);
2098
2099     if (res != NULL) {
2100         if (res->type == XPATH_BOOLEAN)
2101             doit = res->boolval;
2102         else {
2103 #ifdef DEBUG_PROCESS
2104             xsltGenericDebug(xsltGenericDebugContext,
2105                 "xsltIf: test didn't evaluate to a boolean\n");
2106 #endif
2107             goto error;
2108         }
2109     }
2110
2111 #ifdef DEBUG_PROCESS
2112     xsltGenericDebug(xsltGenericDebugContext,
2113         "xsltIf: test evaluate to %d\n", doit);
2114 #endif
2115     if (doit) {
2116         varsPush(ctxt, NULL);
2117         xsltApplyOneTemplate(ctxt, node, inst->children, 0);
2118         xsltFreeStackElemList(varsPop(ctxt));
2119     }
2120
2121 error:
2122     if (xpathParserCtxt != NULL)
2123         xmlXPathFreeParserContext(xpathParserCtxt);
2124     if (prop != NULL)
2125         xmlFree(prop);
2126     if (res != NULL)
2127         xmlXPathFreeObject(res);
2128 }
2129
2130 /**
2131  * xsltForEach:
2132  * @ctxt:  a XSLT process context
2133  * @node:  the node in the source tree.
2134  * @inst:  the xslt for-each node
2135  *
2136  * Process the xslt for-each node on the source node
2137  */
2138 void
2139 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
2140                    xmlNodePtr inst) {
2141     xmlChar *prop;
2142     xmlXPathObjectPtr res = NULL, tmp;
2143     xmlNodePtr replacement;
2144     xmlNodeSetPtr list = NULL, oldlist;
2145     xmlXPathParserContextPtr xpathParserCtxt = NULL;
2146     int i, oldProximityPosition, oldContextSize;
2147
2148     if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2149         return;
2150
2151     prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
2152     if (prop == NULL) {
2153         xsltGenericError(xsltGenericErrorContext,
2154              "xsltForEach: select is not defined\n");
2155         return;
2156     }
2157 #ifdef DEBUG_PROCESS
2158     xsltGenericDebug(xsltGenericDebugContext,
2159          "xsltForEach: select %s\n", prop);
2160 #endif
2161
2162     xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
2163     if (xpathParserCtxt == NULL)
2164         goto error;
2165     ctxt->xpathCtxt->node = node;
2166     xmlXPathEvalExpr(xpathParserCtxt);
2167     res = valuePop(xpathParserCtxt);
2168     do {
2169         tmp = valuePop(xpathParserCtxt);
2170         if (tmp != NULL) {
2171             xmlXPathFreeObject(tmp);
2172         }
2173     } while (tmp != NULL);
2174
2175     if (res != NULL) {
2176         if (res->type == XPATH_NODESET)
2177             list = res->nodesetval;
2178         else {
2179 #ifdef DEBUG_PROCESS
2180             xsltGenericDebug(xsltGenericDebugContext,
2181                 "xsltForEach: select didn't evaluate to a node list\n");
2182 #endif
2183             goto error;
2184         }
2185     }
2186
2187 #ifdef DEBUG_PROCESS
2188     xsltGenericDebug(xsltGenericDebugContext,
2189         "xsltForEach: select evaluate to %d nodes\n", list->nodeNr);
2190 #endif
2191
2192     oldlist = ctxt->nodeList;
2193     ctxt->nodeList = list;
2194     oldContextSize = ctxt->xpathCtxt->contextSize;
2195     oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2196     ctxt->xpathCtxt->contextSize = list->nodeNr;
2197
2198     /* 
2199      * handle and skip the xsl:sort
2200      */
2201     replacement = inst->children;
2202     while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
2203         xsltSort(ctxt, node, replacement);
2204         replacement = replacement->next;
2205     }
2206
2207     for (i = 0;i < list->nodeNr;i++) {
2208         ctxt->node = list->nodeTab[i];
2209         ctxt->xpathCtxt->proximityPosition = i + 1;
2210         varsPush(ctxt, NULL);
2211         xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement, 0);
2212         xsltFreeStackElemList(varsPop(ctxt));
2213     }
2214     ctxt->nodeList = oldlist;
2215     ctxt->xpathCtxt->contextSize = oldContextSize;
2216     ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2217
2218 error:
2219     if (xpathParserCtxt != NULL)
2220         xmlXPathFreeParserContext(xpathParserCtxt);
2221     if (prop != NULL)
2222         xmlFree(prop);
2223     if (res != NULL)
2224         xmlXPathFreeObject(res);
2225 }
2226
2227 /**
2228  * xsltProcessOneNode:
2229  * @ctxt:  a XSLT process context
2230  * @node:  the node in the source tree.
2231  *
2232  * Process the source node.
2233  */
2234 void
2235 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
2236     xsltTemplatePtr template;
2237     xmlNodePtr oldNode;
2238
2239     template = xsltGetTemplate(ctxt, node, NULL);
2240     /*
2241      * If no template is found, apply the default rule.
2242      */
2243     if (template == NULL) {
2244 #ifdef DEBUG_PROCESS
2245         if (node->type == XML_DOCUMENT_NODE)
2246             xsltGenericDebug(xsltGenericDebugContext,
2247              "xsltProcessOneNode: no template found for /\n");
2248         else if (node->type == XML_CDATA_SECTION_NODE)
2249             xsltGenericDebug(xsltGenericDebugContext,
2250              "xsltProcessOneNode: no template found for CDATA\n");
2251         else 
2252             xsltGenericDebug(xsltGenericDebugContext,
2253              "xsltProcessOneNode: no template found for %s\n", node->name);
2254 #endif
2255         oldNode = ctxt->node;
2256         ctxt->node = node;
2257         xsltDefaultProcessOneNode(ctxt, node);
2258         ctxt->node = oldNode;
2259         return;
2260     }
2261
2262     if (node->type == XML_ATTRIBUTE_NODE) {
2263 #ifdef DEBUG_PROCESS
2264         xsltGenericDebug(xsltGenericDebugContext,
2265              "xsltProcessOneNode: applying template for attribute %s\n",
2266                          node->name);
2267 #endif
2268         templPush(ctxt, template);
2269         varsPush(ctxt, NULL);
2270         xsltApplyOneTemplate(ctxt, node, template->content, 1);
2271         xsltFreeStackElemList(varsPop(ctxt));
2272         templPop(ctxt);
2273     } else {
2274 #ifdef DEBUG_PROCESS
2275         if (node->type == XML_DOCUMENT_NODE)
2276             xsltGenericDebug(xsltGenericDebugContext,
2277              "xsltProcessOneNode: applying template for /\n");
2278         else 
2279             xsltGenericDebug(xsltGenericDebugContext,
2280              "xsltProcessOneNode: applying template for %s\n", node->name);
2281 #endif
2282         oldNode = ctxt->node;
2283         ctxt->node = node;
2284         templPush(ctxt, template);
2285         varsPush(ctxt, NULL);
2286         xsltApplyOneTemplate(ctxt, node, template->content, 1);
2287         xsltFreeStackElemList(varsPop(ctxt));
2288         templPop(ctxt);
2289         ctxt->node = oldNode;
2290     }
2291 }
2292
2293 /**
2294  * xsltApplyStylesheet:
2295  * @style:  a parsed XSLT stylesheet
2296  * @doc:  a parsed XML document
2297  *
2298  * Apply the stylesheet to the document
2299  * NOTE: This may lead to a non-wellformed output XML wise !
2300  *
2301  * Returns the result document or NULL in case of error
2302  */
2303 xmlDocPtr
2304 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc) {
2305     xmlDocPtr res = NULL;
2306     xsltTransformContextPtr ctxt = NULL;
2307     xmlNodePtr root;
2308
2309     if ((style == NULL) || (doc == NULL))
2310         return(NULL);
2311     ctxt = xsltNewTransformContext(style, doc);
2312     if (ctxt == NULL)
2313         return(NULL);
2314     if ((style->method != NULL) &&
2315         (!xmlStrEqual(style->method, (const xmlChar *) "xml"))) {
2316         if (xmlStrEqual(style->method, (const xmlChar *) "html")) {
2317             ctxt->type = XSLT_OUTPUT_HTML;
2318             res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
2319             if (res == NULL)
2320                 goto error;
2321         } else if (xmlStrEqual(style->method, (const xmlChar *) "xhtml")) {
2322             xsltGenericError(xsltGenericErrorContext,
2323              "xsltApplyStylesheet: insupported method xhtml, using html\n",
2324                              style->method);
2325             ctxt->type = XSLT_OUTPUT_HTML;
2326             res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
2327             if (res == NULL)
2328                 goto error;
2329         } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
2330             ctxt->type = XSLT_OUTPUT_TEXT;
2331             res = xmlNewDoc(style->version);
2332             if (res == NULL)
2333                 goto error;
2334         } else {
2335             xsltGenericError(xsltGenericErrorContext,
2336                              "xsltApplyStylesheet: insupported method %s\n",
2337                              style->method);
2338             goto error;
2339         }
2340     } else {
2341         ctxt->type = XSLT_OUTPUT_XML;
2342         res = xmlNewDoc(style->version);
2343         if (res == NULL)
2344             goto error;
2345     }
2346     res->charset = XML_CHAR_ENCODING_UTF8;
2347     if (style->encoding != NULL)
2348         res->encoding = xmlStrdup(style->encoding);
2349
2350     /*
2351      * Start.
2352      */
2353     ctxt->output = res;
2354     ctxt->insert = (xmlNodePtr) res;
2355     xsltEvalGlobalVariables(ctxt);
2356     ctxt->node = (xmlNodePtr) doc;
2357     varsPush(ctxt, NULL);
2358     xsltProcessOneNode(ctxt, ctxt->node);
2359     xsltFreeStackElemList(varsPop(ctxt));
2360
2361
2362     if ((ctxt->type = XSLT_OUTPUT_XML) &&
2363         ((style->doctypePublic != NULL) ||
2364          (style->doctypeSystem != NULL))) {
2365         root = xmlDocGetRootElement(res);
2366         if (root != NULL)
2367             res->intSubset = xmlCreateIntSubset(res, root->name,
2368                          style->doctypePublic, style->doctypeSystem);
2369     }
2370     xmlXPathFreeNodeSet(ctxt->nodeList);
2371     xsltFreeTransformContext(ctxt);
2372     return(res);
2373
2374 error:
2375     if (res != NULL)
2376         xmlFreeDoc(res);
2377     if (ctxt != NULL)
2378         xsltFreeTransformContext(ctxt);
2379     return(NULL);
2380 }
2381