Some infrastructure work, and of course some debug:
[platform/upstream/libxslt.git] / libxslt / xslt.c
1 /*
2  * xslt.c: Implemetation of an XSL Transformation 1.0 engine
3  *
4  * Reference:
5  *   http://www.w3.org/TR/1999/REC-xslt-19991116
6  *
7  * See Copyright for the status of this software.
8  *
9  * Daniel.Veillard@imag.fr
10  */
11
12 #include "xsltconfig.h"
13
14 #include <string.h>
15
16 #include <libxml/xmlmemory.h>
17 #include <libxml/parser.h>
18 #include <libxml/tree.h>
19 #include <libxml/valid.h>
20 #include <libxml/hash.h>
21 #include <libxml/xmlerror.h>
22 #include "xslt.h"
23 #include "xsltInternals.h"
24 #include "pattern.h"
25 #include "xsltutils.h"
26
27 #define DEBUG_PARSING
28
29 /*
30  * Useful macros
31  */
32
33 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) ||  \
34                      ((c) == 0x0D))
35
36 #define IS_BLANK_NODE(n)                                                \
37     (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
38
39
40 /************************************************************************
41  *                                                                      *
42  *                      Helper functions                                *
43  *                                                                      *
44  ************************************************************************/
45
46 /**
47  * xsltIsBlank:
48  * @str:  a string
49  *
50  * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
51  */
52 int
53 xsltIsBlank(xmlChar *str) {
54     if (str == NULL)
55         return(1);
56     while (*str != 0) {
57         if (!(IS_BLANK(*str))) return(0);
58         str++;
59     }
60     return(1);
61 }
62
63 /************************************************************************
64  *                                                                      *
65  *              Routines to handle XSLT data structures                 *
66  *                                                                      *
67  ************************************************************************/
68
69 /**
70  * xsltNewTemplate:
71  *
72  * Create a new XSLT Template
73  *
74  * Returns the newly allocated xsltTemplatePtr or NULL in case of error
75  */
76 xsltTemplatePtr
77 xsltNewTemplate(void) {
78     xsltTemplatePtr cur;
79
80     cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate));
81     if (cur == NULL) {
82         xsltGenericError(xsltGenericErrorContext,
83                 "xsltNewTemplate : malloc failed\n");
84         return(NULL);
85     }
86     memset(cur, 0, sizeof(xsltTemplate));
87     cur->priority = XSLT_PAT_NO_PRIORITY;
88     return(cur);
89 }
90
91 /**
92  * xsltFreeTemplate:
93  * @template:  an XSLT template
94  *
95  * Free up the memory allocated by @template
96  */
97 void
98 xsltFreeTemplate(xsltTemplatePtr template) {
99     if (template == NULL)
100         return;
101     if (template->match) xmlFree(template->match);
102     if (template->name) xmlFree(template->name);
103     if (template->nameURI) xmlFree(template->nameURI);
104     if (template->mode) xmlFree(template->mode);
105     if (template->modeURI) xmlFree(template->modeURI);
106     memset(template, -1, sizeof(xsltTemplate));
107     xmlFree(template);
108 }
109
110 /**
111  * xsltFreeTemplateList:
112  * @template:  an XSLT template list
113  *
114  * Free up the memory allocated by all the elements of @template
115  */
116 void
117 xsltFreeTemplateList(xsltTemplatePtr template) {
118     xsltTemplatePtr cur;
119
120     while (template != NULL) {
121         cur = template;
122         template = template->next;
123         xsltFreeTemplate(cur);
124     }
125 }
126
127 /**
128  * xsltNewStylesheet:
129  *
130  * Create a new XSLT Stylesheet
131  *
132  * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
133  */
134 xsltStylesheetPtr
135 xsltNewStylesheet(void) {
136     xsltStylesheetPtr cur;
137
138     cur = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet));
139     if (cur == NULL) {
140         xsltGenericError(xsltGenericErrorContext,
141                 "xsltNewStylesheet : malloc failed\n");
142         return(NULL);
143     }
144     memset(cur, 0, sizeof(xsltStylesheet));
145     cur->omitXmlDeclaration = -1;
146     cur->standalone = -1;
147     cur->indent = -1;
148     return(cur);
149 }
150
151 /**
152  * xsltFreeStylesheet:
153  * @sheet:  an XSLT stylesheet
154  *
155  * Free up the memory allocated by @sheet
156  */
157 void
158 xsltFreeStylesheet(xsltStylesheetPtr sheet) {
159     if (sheet == NULL)
160         return;
161
162     xsltFreeTemplateHashes(sheet);
163     xsltFreeTemplateList(sheet->templates);
164     if (sheet->doc != NULL)
165         xmlFreeDoc(sheet->doc);
166     if (sheet->stripSpaces != NULL)
167         xmlHashFree(sheet->stripSpaces, NULL);
168
169     if (sheet->method != NULL) xmlFree(sheet->method);
170     if (sheet->methodURI != NULL) xmlFree(sheet->methodURI);
171     if (sheet->version != NULL) xmlFree(sheet->version);
172     if (sheet->encoding != NULL) xmlFree(sheet->encoding);
173     if (sheet->doctypePublic != NULL) xmlFree(sheet->doctypePublic);
174     if (sheet->doctypeSystem != NULL) xmlFree(sheet->doctypeSystem);
175     if (sheet->mediaType != NULL) xmlFree(sheet->mediaType);
176
177     memset(sheet, -1, sizeof(xsltStylesheet));
178     xmlFree(sheet);
179 }
180
181 /************************************************************************
182  *                                                                      *
183  *              Parsing of an XSLT Stylesheet                           *
184  *                                                                      *
185  ************************************************************************/
186
187 /**
188  * xsltParseStylesheetOutput:
189  * @style:  the XSLT stylesheet
190  * @template:  the "output" element
191  *
192  * parse an XSLT stylesheet output element and record
193  * information related to the stylesheet output
194  */
195
196 void
197 xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) {
198     xmlChar *elements, *prop;
199     xmlChar *element, *end;
200
201     if ((cur == NULL) || (style == NULL))
202         return;
203
204     prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE);
205     if (prop != NULL) {
206         if (style->version != NULL) xmlFree(style->version);
207         style->version  = prop;
208     }
209
210     prop = xmlGetNsProp(cur, (const xmlChar *)"encoding", XSLT_NAMESPACE);
211     if (prop != NULL) {
212         if (style->encoding != NULL) xmlFree(style->encoding);
213         style->encoding  = prop;
214     }
215
216     prop = xmlGetNsProp(cur, (const xmlChar *)"method", XSLT_NAMESPACE);
217     if (prop != NULL) {
218         xmlChar *ncname;
219         xmlChar *prefix = NULL;
220
221         if (style->method != NULL) xmlFree(style->method);
222         style->method = NULL;
223         if (style->methodURI != NULL) xmlFree(style->methodURI);
224         style->methodURI = NULL;
225
226         ncname = xmlSplitQName2(prop, &prefix);
227         if (ncname != NULL) {
228             if (prefix != NULL) {
229                 xmlNsPtr ns;
230
231                 ns = xmlSearchNs(cur->doc, cur, prefix);
232                 if (ns == NULL) {
233                     xsltGenericError(xsltGenericErrorContext,
234                         "no namespace bound to prefix %s\n", prefix);
235                     xmlFree(prefix);
236                     xmlFree(ncname);
237                     style->method = prop;
238                 } else {
239                     style->methodURI = xmlStrdup(ns->href);
240                     style->method = ncname;
241                     xmlFree(prefix);
242                     xmlFree(prop);
243                 }
244             } else {
245                 style->method = ncname;
246                 xmlFree(prop);
247             }
248         } else {
249             if ((xmlStrEqual(prop, (const xmlChar *)"xml")) ||
250                 (xmlStrEqual(prop, (const xmlChar *)"html")) ||
251                 (xmlStrEqual(prop, (const xmlChar *)"text"))) {
252                 style->method  = prop;
253             } else {
254                 xsltGenericError(xsltGenericErrorContext,
255                     "invalid value for method: %s\n", prop);
256             }
257         }
258     }
259
260     prop = xmlGetNsProp(cur, (const xmlChar *)"doctype-system", XSLT_NAMESPACE);
261     if (prop != NULL) {
262         if (style->doctypeSystem != NULL) xmlFree(style->doctypeSystem);
263         style->doctypeSystem  = prop;
264     }
265
266     prop = xmlGetNsProp(cur, (const xmlChar *)"doctype-public", XSLT_NAMESPACE);
267     if (prop != NULL) {
268         if (style->doctypePublic != NULL) xmlFree(style->doctypePublic);
269         style->doctypePublic  = prop;
270     }
271
272     prop = xmlGetNsProp(cur, (const xmlChar *)"standalone",
273                         XSLT_NAMESPACE);
274     if (prop != NULL) {
275         if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
276             style->standalone = 1;
277         } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
278             style->standalone = 0;
279         } else {
280             xsltGenericError(xsltGenericErrorContext,
281                 "invalid value for standalone: %s\n", prop);
282         }
283         xmlFree(prop);
284     }
285
286     prop = xmlGetNsProp(cur, (const xmlChar *)"indent",
287                         XSLT_NAMESPACE);
288     if (prop != NULL) {
289         if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
290             style->indent = 1;
291         } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
292             style->indent = 0;
293         } else {
294             xsltGenericError(xsltGenericErrorContext,
295                 "invalid value for indent: %s\n", prop);
296         }
297         xmlFree(prop);
298     }
299
300     prop = xmlGetNsProp(cur, (const xmlChar *)"omit-xml-declaration",
301                         XSLT_NAMESPACE);
302     if (prop != NULL) {
303         if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
304             style->omitXmlDeclaration = 1;
305         } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
306             style->omitXmlDeclaration = 0;
307         } else {
308             xsltGenericError(xsltGenericErrorContext,
309                 "invalid value for omit-xml-declaration: %s\n", prop);
310         }
311         xmlFree(prop);
312     }
313
314     elements = xmlGetNsProp(cur, (const xmlChar *)"cdata-section-elements",
315                             XSLT_NAMESPACE);
316     if (elements != NULL) {
317         if (style->stripSpaces == NULL)
318             style->stripSpaces = xmlHashCreate(10);
319         if (style->stripSpaces == NULL)
320             return;
321
322         element = elements;
323         while (*element != 0) {
324             while (IS_BLANK(*element)) element++;
325             if (*element == 0)
326                 break;
327             end = element;
328             while ((*end != 0) && (!IS_BLANK(*end))) end++;
329             element = xmlStrndup(element, end - element);
330             if (element) {
331 #ifdef DEBUG_PARSING
332                 xsltGenericDebug(xsltGenericDebugContext,
333                     "add cdata section output element %s\n", element);
334 #endif
335                 xmlHashAddEntry(style->stripSpaces, element, "cdata");
336                 xmlFree(element);
337             }
338             element = end;
339         }
340         xmlFree(elements);
341     }
342 }
343
344 /**
345  * xsltParseStylesheetPreserveSpace:
346  * @style:  the XSLT stylesheet
347  * @template:  the "preserve-space" element
348  *
349  * parse an XSLT stylesheet preserve-space element and record
350  * elements needing preserving
351  */
352
353 void
354 xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
355     xmlChar *elements;
356     xmlChar *element, *end;
357
358     if ((cur == NULL) || (style == NULL))
359         return;
360
361     elements = xmlGetNsProp(cur, (const xmlChar *)"elements", XSLT_NAMESPACE);
362     if (elements == NULL) {
363         xsltGenericError(xsltGenericErrorContext,
364             "xsltParseStylesheetPreserveSpace: missing elements attribute\n");
365         return;
366     }
367
368     if (style->stripSpaces == NULL)
369         style->stripSpaces = xmlHashCreate(10);
370     if (style->stripSpaces == NULL)
371         return;
372
373     element = elements;
374     while (*element != 0) {
375         while (IS_BLANK(*element)) element++;
376         if (*element == 0)
377             break;
378         end = element;
379         while ((*end != 0) && (!IS_BLANK(*end))) end++;
380         element = xmlStrndup(element, end - element);
381         if (element) {
382 #ifdef DEBUG_PARSING
383             xsltGenericDebug(xsltGenericDebugContext,
384                 "add preserved space element %s\n", element);
385 #endif
386             xmlHashAddEntry(style->stripSpaces, element, "preserve");
387             xmlFree(element);
388         }
389         element = end;
390     }
391     xmlFree(elements);
392 }
393
394 /**
395  * xsltParseStylesheetStripSpace:
396  * @style:  the XSLT stylesheet
397  * @template:  the "strip-space" element
398  *
399  * parse an XSLT stylesheet strip-space element and record
400  * elements needing stripping
401  */
402
403 void
404 xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
405     xmlChar *elements;
406     xmlChar *element, *end;
407
408     if ((cur == NULL) || (style == NULL))
409         return;
410
411     elements = xmlGetNsProp(cur, (const xmlChar *)"elements", XSLT_NAMESPACE);
412     if (elements == NULL) {
413         xsltGenericError(xsltGenericErrorContext,
414             "xsltParseStylesheetStripSpace: missing elements attribute\n");
415         return;
416     }
417
418     if (style->stripSpaces == NULL)
419         style->stripSpaces = xmlHashCreate(10);
420     if (style->stripSpaces == NULL)
421         return;
422
423     element = elements;
424     while (*element != 0) {
425         while (IS_BLANK(*element)) element++;
426         if (*element == 0)
427             break;
428         end = element;
429         while ((*end != 0) && (!IS_BLANK(*end))) end++;
430         element = xmlStrndup(element, end - element);
431         if (element) {
432 #ifdef DEBUG_PARSING
433             xsltGenericDebug(xsltGenericDebugContext,
434                 "add stripped space element %s\n", element);
435 #endif
436             xmlHashAddEntry(style->stripSpaces, element, "strip");
437             xmlFree(element);
438         }
439         element = end;
440     }
441     xmlFree(elements);
442 }
443
444 /**
445  * xsltParseTemplateContent:
446  * @style:  the XSLT stylesheet
447  * @ret:  the "template" structure
448  * @template:  the container node (can be a document for literal results)
449  *
450  * parse an XSLT template element content
451  * Clean-up the template content from unwanted ignorable blank nodes
452  * and process xslt:text
453  */
454
455 void
456 xsltParseTemplateContent(xsltStylesheetPtr style, xsltTemplatePtr ret,
457                          xmlNodePtr template) {
458     xmlNodePtr cur, delete;
459     /*
460      * This content comes from the stylesheet
461      * For stylesheets, the set of whitespace-preserving
462      * element names consists of just xsl:text.
463      */
464     cur = template->children;
465     delete = NULL;
466     while (cur != NULL) {
467         if (delete != NULL) {
468 #ifdef DEBUG_PARSING
469             xsltGenericDebug(xsltGenericDebugContext,
470              "xsltParseStylesheetTemplate: removing ignorable blank node\n");
471 #endif
472             xmlUnlinkNode(delete);
473             xmlFreeNode(delete);
474             delete = NULL;
475         }
476         if (IS_XSLT_ELEM(cur)) {
477             if (IS_XSLT_NAME(cur, "text")) {
478                 if (cur->children != NULL) {
479                     if ((cur->children->type != XML_TEXT_NODE) ||
480                         (cur->children->next != NULL)) {
481                         xsltGenericError(xsltGenericErrorContext,
482              "xsltParseStylesheetTemplate: xslt:text content problem\n");
483                     } else {
484                         xmlNodePtr text = cur->children;
485                         xmlUnlinkNode(text);
486                         xmlAddPrevSibling(cur, text);
487                     }
488                 }
489                 delete = cur;
490                 goto skip_children;
491             }
492         } else if (cur->type == XML_TEXT_NODE) {
493             if (IS_BLANK_NODE(cur)) {
494                 delete = cur;
495             }
496         } else if (cur->type != XML_ELEMENT_NODE) {
497             delete = cur;
498         }
499
500         /*
501          * Skip to next node
502          */
503         if (cur->children != NULL) {
504             if (cur->children->type != XML_ENTITY_DECL) {
505                 cur = cur->children;
506                 continue;
507             }
508         }
509 skip_children:
510         if (cur->next != NULL) {
511             cur = cur->next;
512             continue;
513         }
514         
515         do {
516             cur = cur->parent;
517             if (cur == NULL)
518                 break;
519             if (cur == template) {
520                 cur = NULL;
521                 break;
522             }
523             if (cur->next != NULL) {
524                 cur = cur->next;
525                 break;
526             }
527         } while (cur != NULL);
528     }
529     if (delete != NULL) {
530 #ifdef DEBUG_PARSING
531         xsltGenericDebug(xsltGenericDebugContext,
532          "xsltParseStylesheetTemplate: removing ignorable blank node\n");
533 #endif
534         xmlUnlinkNode(delete);
535         xmlFreeNode(delete);
536         delete = NULL;
537     }
538
539     /*
540      * Find and handle the params
541      */
542     cur = template->children;
543     while (cur != NULL) {
544         /*
545          * Remove Blank nodes found at this level.
546          */
547         if (IS_BLANK_NODE(cur)) {
548             xmlNodePtr blank = cur;
549
550             cur = cur->next;
551             xmlUnlinkNode(blank);
552             xmlFreeNode(blank);
553             continue;
554         }
555         if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
556             TODO /* Handle param */
557         } else
558             break;
559         cur = cur->next;
560     }
561
562     /*
563      * Browse the remaining of the template
564      */
565     while (cur != NULL) {
566         /*
567          * Remove Blank nodes found at this level.
568          */
569         if (IS_BLANK_NODE(cur)) {
570             xmlNodePtr blank = cur;
571
572             cur = cur->next;
573             xmlUnlinkNode(blank);
574             xmlFreeNode(blank);
575             continue;
576         }
577         if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
578             xmlNodePtr param = cur;
579
580             cur = cur->next;
581             xsltGenericError(xsltGenericErrorContext,
582                 "xsltParseStylesheetTop: ignoring misplaced param element\n");
583             xmlUnlinkNode(param);
584             xmlFreeNode(param);
585             continue;
586         } else
587             break;
588         cur = cur->next;
589     }
590
591     ret->content = template->children;
592 }
593
594 /**
595  * xsltParseStylesheetTemplate:
596  * @style:  the XSLT stylesheet
597  * @template:  the "template" element
598  *
599  * parse an XSLT stylesheet template building the associated structures
600  */
601
602 void
603 xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) {
604     xsltTemplatePtr ret;
605     xmlChar *prop;
606
607     if (template == NULL)
608         return;
609
610     /*
611      * Create and link the structure
612      */
613     ret = xsltNewTemplate();
614     if (ret == NULL)
615         return;
616     ret->next = style->templates;
617     style->templates = ret;
618
619     /*
620      * Get arguments
621      */
622     prop = xmlGetNsProp(template, (const xmlChar *)"match", XSLT_NAMESPACE);
623     if (prop != NULL) {
624         if (ret->match != NULL) xmlFree(ret->match);
625         ret->match  = prop;
626     }
627
628     prop = xmlGetNsProp(template, (const xmlChar *)"name", XSLT_NAMESPACE);
629     if (prop != NULL) {
630         xmlChar *ncname;
631         xmlChar *prefix = NULL;
632
633         if (ret->name != NULL) xmlFree(ret->name);
634         ret->name = NULL;
635         if (ret->nameURI != NULL) xmlFree(ret->nameURI);
636         ret->nameURI = NULL;
637
638         ncname = xmlSplitQName2(prop, &prefix);
639         if (ncname != NULL) {
640             if (prefix != NULL) {
641                 xmlNsPtr ns;
642
643                 ns = xmlSearchNs(template->doc, template, prefix);
644                 if (ns == NULL) {
645                     xsltGenericError(xsltGenericErrorContext,
646                         "no namespace bound to prefix %s\n", prefix);
647                     xmlFree(prefix);
648                     xmlFree(ncname);
649                     ret->name = prop;
650                 } else {
651                     ret->nameURI = xmlStrdup(ns->href);
652                     ret->name = ncname;
653                     xmlFree(prefix);
654                     xmlFree(prop);
655                 }
656             } else {
657                 ret->name = ncname;
658                 xmlFree(prop);
659             }
660         } else {
661             ret->name  = prop;
662         }
663     }
664
665     /*
666      * parse the content and register the pattern
667      */
668     xsltParseTemplateContent(style, ret, template);
669     xsltAddTemplate(style, ret);
670 }
671
672 /**
673  * xsltParseStylesheetTop:
674  * @style:  the XSLT stylesheet
675  * @top:  the top level "stylesheet" element
676  *
677  * scan the top level elements of an XSL stylesheet
678  */
679
680 void
681 xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) {
682     xmlNodePtr cur;
683 #ifdef DEBUG_PARSING
684     int templates = 0;
685 #endif
686
687     if (top == NULL)
688         return;
689     cur = top->children;
690
691     while (cur != NULL) {
692         if (IS_BLANK_NODE(cur)) {
693             cur = cur->next;
694             continue;
695         }
696         if (!(IS_XSLT_ELEM(cur))) {
697 #ifdef DEBUG_PARSING
698             xsltGenericDebug(xsltGenericDebugContext,
699                     "xsltParseStylesheetTop : found foreign element %s\n",
700                     cur->name);
701 #endif
702             cur = cur->next;
703             continue;
704         }
705         if (IS_XSLT_NAME(cur, "import")) {
706             TODO /* Handle import */
707         } else
708             break;
709         cur = cur->next;
710     }
711     while (cur != NULL) {
712         if (IS_BLANK_NODE(cur)) {
713             cur = cur->next;
714             continue;
715         }
716         if (!(IS_XSLT_ELEM(cur))) {
717 #ifdef DEBUG_PARSING
718             xsltGenericDebug(xsltGenericDebugContext,
719                     "xsltParseStylesheetTop : found foreign element %s\n",
720                     cur->name);
721 #endif
722             cur = cur->next;
723             continue;
724         }
725         if (IS_XSLT_NAME(cur, "import")) {
726             xsltGenericError(xsltGenericErrorContext,
727                 "xsltParseStylesheetTop: ignoring misplaced import element\n");
728         } else if (IS_XSLT_NAME(cur, "include")) {
729             TODO /* Handle include */
730         } else if (IS_XSLT_NAME(cur, "strip-space")) {
731             xsltParseStylesheetStripSpace(style, cur);
732         } else if (IS_XSLT_NAME(cur, "preserve-space")) {
733             xsltParseStylesheetPreserveSpace(style, cur);
734         } else if (IS_XSLT_NAME(cur, "output")) {
735             xsltParseStylesheetOutput(style, cur);
736         } else if (IS_XSLT_NAME(cur, "key")) {
737             TODO /* Handle key */
738         } else if (IS_XSLT_NAME(cur, "decimal-format")) {
739             TODO /* Handle decimal-format */
740         } else if (IS_XSLT_NAME(cur, "attribute-set")) {
741             TODO /* Handle attribute-set */
742         } else if (IS_XSLT_NAME(cur, "variable")) {
743             TODO /* Handle variable */
744         } else if (IS_XSLT_NAME(cur, "param")) {
745             TODO /* Handle param */
746         } else if (IS_XSLT_NAME(cur, "template")) {
747 #ifdef DEBUG_PARSING
748             templates++;
749 #endif
750             xsltParseStylesheetTemplate(style, cur);
751         } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
752             TODO /* Handle namespace-alias */
753         } else {
754             xsltGenericError(xsltGenericErrorContext,
755                 "xsltParseStylesheetTop: ignoring unknown %s element\n",
756                              cur->name);
757         }
758         cur = cur->next;
759     }
760 #ifdef DEBUG_PARSING
761     xsltGenericDebug(xsltGenericDebugContext,
762                     "parsed %d templates\n", templates);
763 #endif
764 }
765
766 /**
767  * xsltParseStylesheetDoc:
768  * @doc:  and xmlDoc parsed XML
769  *
770  * parse an XSLT stylesheet building the associated structures
771  *
772  * Returns a new XSLT stylesheet structure.
773  */
774
775 xsltStylesheetPtr
776 xsltParseStylesheetDoc(xmlDocPtr doc) {
777     xsltStylesheetPtr ret;
778     xmlNodePtr cur;
779
780     if (doc == NULL)
781         return(NULL);
782
783     ret = xsltNewStylesheet();
784     if (ret == NULL)
785         return(NULL);
786
787     /*
788      * First step, locate the xsl:stylesheet element and the
789      * namespace declaration.
790      */
791     cur = xmlDocGetRootElement(doc);
792     if (cur == NULL) {
793         xsltGenericError(xsltGenericErrorContext,
794                 "xsltParseStylesheetDoc : empty stylesheet\n");
795         xsltFreeStylesheet(ret);
796         return(NULL);
797     }
798
799     ret->doc = doc;
800     if ((IS_XSLT_ELEM(cur)) && 
801         ((IS_XSLT_NAME(cur, "stylesheet")) ||
802          (IS_XSLT_NAME(cur, "transform")))) {
803 #ifdef DEBUG_PARSING
804         xsltGenericDebug(xsltGenericDebugContext,
805                 "xsltParseStylesheetDoc : found stylesheet\n");
806 #endif
807
808         xsltParseStylesheetTop(ret, cur);
809     } else {
810         xmlChar *prop;
811         xsltTemplatePtr template;
812
813         /*
814          * the document itself might be the template, check xsl:version
815          */
816         prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE);
817         if (prop == NULL) {
818             xsltGenericError(xsltGenericErrorContext,
819                 "xsltParseStylesheetDoc : document is not a stylesheet\n");
820             xsltFreeStylesheet(ret);
821             return(NULL);
822         }
823
824 #ifdef DEBUG_PARSING
825         xsltGenericDebug(xsltGenericDebugContext,
826                 "xsltParseStylesheetDoc : document is stylesheet\n");
827 #endif
828         
829         /* TODO: check the version */
830         xmlFree(prop);
831
832         /*
833          * Create and link the template
834          */
835         template = xsltNewTemplate();
836         if (template == NULL) {
837             xsltFreeStylesheet(ret);
838             return(NULL);
839         }
840         template->next = ret->templates;
841         ret->templates = template;
842         template->match = xmlStrdup((const xmlChar *)"/");
843
844         /*
845          * parse the content and register the pattern
846          */
847         xsltParseTemplateContent(ret, template, (xmlNodePtr) doc);
848         xsltAddTemplate(ret, template);
849     }
850
851     return(ret);
852 }
853
854 /**
855  * xsltParseStylesheetFile:
856  * @filename:  the filename/URL to the stylesheet
857  *
858  * Load and parse an XSLT stylesheet
859  *
860  * Returns a new XSLT stylesheet structure.
861  */
862
863 xsltStylesheetPtr
864 xsltParseStylesheetFile(const xmlChar* filename) {
865     xsltStylesheetPtr ret;
866     xmlDocPtr doc;
867     
868
869     if (filename == NULL)
870         return(NULL);
871
872 #ifdef DEBUG_PARSING
873     xsltGenericDebug(xsltGenericDebugContext,
874             "xsltParseStylesheetFile : parse %s\n", filename);
875 #endif
876
877     doc = xmlParseFile((const char *) filename);
878     if (doc == NULL) {
879         xsltGenericError(xsltGenericErrorContext,
880                 "xsltParseStylesheetFile : cannot parse %s\n", filename);
881         return(NULL);
882     }
883     ret = xsltParseStylesheetDoc(doc);
884     if (ret == NULL) {
885         xmlFreeDoc(doc);
886         return(NULL);
887     }
888
889     return(ret);
890 }
891