xslt: Import upstream release 1.1.38.
[wine.git] / libs / xslt / libxslt / xslt.c
blob39a700b0c3f132dd45ded52c1d15ed7ee94974ca
1 /*
2 * xslt.c: Implemetation of an XSL Transformation 1.0 engine
4 * Reference:
5 * XSLT specification
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
8 * Associating Style Sheets with XML documents
9 * http://www.w3.org/1999/06/REC-xml-stylesheet-19990629
11 * See Copyright for the status of this software.
13 * daniel@veillard.com
16 #define IN_LIBXSLT
17 #include "libxslt.h"
19 #include <string.h>
21 #include <libxml/xmlmemory.h>
22 #include <libxml/parser.h>
23 #include <libxml/tree.h>
24 #include <libxml/valid.h>
25 #include <libxml/hash.h>
26 #include <libxml/uri.h>
27 #include <libxml/xmlerror.h>
28 #include <libxml/parserInternals.h>
29 #include <libxml/xpathInternals.h>
30 #include <libxml/xpath.h>
31 #include "xslt.h"
32 #include "xsltInternals.h"
33 #include "pattern.h"
34 #include "variables.h"
35 #include "namespaces.h"
36 #include "attributes.h"
37 #include "xsltutils.h"
38 #include "imports.h"
39 #include "keys.h"
40 #include "documents.h"
41 #include "extensions.h"
42 #include "preproc.h"
43 #include "extra.h"
44 #include "security.h"
45 #include "xsltlocale.h"
47 #ifdef WITH_XSLT_DEBUG
48 #define WITH_XSLT_DEBUG_PARSING
49 /* #define WITH_XSLT_DEBUG_BLANKS */
50 #endif
52 const char *xsltEngineVersion = LIBXSLT_VERSION_STRING LIBXSLT_VERSION_EXTRA;
53 const int xsltLibxsltVersion = LIBXSLT_VERSION;
54 const int xsltLibxmlVersion = LIBXML_VERSION;
56 #ifdef XSLT_REFACTORED
58 const xmlChar *xsltConstNamespaceNameXSLT = (const xmlChar *) XSLT_NAMESPACE;
60 #define XSLT_ELEMENT_CATEGORY_XSLT 0
61 #define XSLT_ELEMENT_CATEGORY_EXTENSION 1
62 #define XSLT_ELEMENT_CATEGORY_LRE 2
65 * xsltLiteralResultMarker:
66 * Marker for Literal result elements, in order to avoid multiple attempts
67 * to recognize such elements in the stylesheet's tree.
68 * This marker is set on node->psvi during the initial traversal
69 * of a stylesheet's node tree.
71 const xmlChar *xsltLiteralResultMarker =
72 (const xmlChar *) "Literal Result Element";
76 * xsltXSLTTextMarker:
77 * Marker for xsl:text elements. Used to recognize xsl:text elements
78 * for post-processing of the stylesheet's tree, where those
79 * elements are removed from the tree.
81 const xmlChar *xsltXSLTTextMarker = (const xmlChar *) "XSLT Text Element";
84 * xsltXSLTAttrMarker:
85 * Marker for XSLT attribute on Literal Result Elements.
87 const xmlChar *xsltXSLTAttrMarker = (const xmlChar *) "LRE XSLT Attr";
89 #endif
91 #ifdef XSLT_LOCALE_WINAPI
92 extern xmlRMutexPtr xsltLocaleMutex;
93 #endif
96 * Useful macros
99 #ifdef IS_BLANK
100 #undef IS_BLANK
101 #endif
102 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
103 ((c) == 0x0D))
105 #ifdef IS_BLANK_NODE
106 #undef IS_BLANK_NODE
107 #endif
108 #define IS_BLANK_NODE(n) \
109 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
112 * xsltParseContentError:
114 * @style: the stylesheet
115 * @node: the node where the error occured
117 * Compile-time error function.
119 static void
120 xsltParseContentError(xsltStylesheetPtr style,
121 xmlNodePtr node)
123 if ((style == NULL) || (node == NULL))
124 return;
126 if (IS_XSLT_ELEM(node))
127 xsltTransformError(NULL, style, node,
128 "The XSLT-element '%s' is not allowed at this position.\n",
129 node->name);
130 else
131 xsltTransformError(NULL, style, node,
132 "The element '%s' is not allowed at this position.\n",
133 node->name);
134 style->errors++;
137 #ifdef XSLT_REFACTORED
138 #else
140 * exclPrefixPush:
141 * @style: the transformation stylesheet
142 * @value: the excluded namespace name to push on the stack
144 * Push an excluded namespace name on the stack
146 * Returns the new index in the stack or -1 if already present or
147 * in case of error
149 static int
150 exclPrefixPush(xsltStylesheetPtr style, xmlChar * value)
152 int i;
154 /* do not push duplicates */
155 for (i = 0;i < style->exclPrefixNr;i++) {
156 if (xmlStrEqual(style->exclPrefixTab[i], value))
157 return(-1);
159 if (style->exclPrefixNr >= style->exclPrefixMax) {
160 xmlChar **tmp;
161 size_t max = style->exclPrefixMax ? style->exclPrefixMax * 2 : 4;
163 tmp = xmlRealloc(style->exclPrefixTab,
164 max * sizeof(style->exclPrefixTab[0]));
165 if (tmp == NULL) {
166 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
167 return (-1);
169 style->exclPrefixTab = tmp;
170 style->exclPrefixMax = max;
172 style->exclPrefixTab[style->exclPrefixNr] = value;
173 style->exclPrefix = value;
174 return (style->exclPrefixNr++);
177 * exclPrefixPop:
178 * @style: the transformation stylesheet
180 * Pop an excluded prefix value from the stack
182 * Returns the stored excluded prefix value
184 static xmlChar *
185 exclPrefixPop(xsltStylesheetPtr style)
187 xmlChar *ret;
189 if (style->exclPrefixNr <= 0)
190 return (0);
191 style->exclPrefixNr--;
192 if (style->exclPrefixNr > 0)
193 style->exclPrefix = style->exclPrefixTab[style->exclPrefixNr - 1];
194 else
195 style->exclPrefix = NULL;
196 ret = style->exclPrefixTab[style->exclPrefixNr];
197 style->exclPrefixTab[style->exclPrefixNr] = 0;
198 return (ret);
200 #endif
202 /************************************************************************
204 * Helper functions *
206 ************************************************************************/
208 static int initialized = 0;
210 * xsltInit:
212 * Initializes the processor (e.g. registers built-in extensions,
213 * etc.)
215 void
216 xsltInit (void) {
217 if (initialized == 0) {
218 initialized = 1;
219 #ifdef XSLT_LOCALE_WINAPI
220 xsltLocaleMutex = xmlNewRMutex();
221 #endif
222 xsltRegisterAllExtras();
227 * xsltUninit:
229 * Uninitializes the processor.
231 void
232 xsltUninit (void) {
233 #ifdef XSLT_LOCALE_WINAPI
234 xmlFreeRMutex(xsltLocaleMutex);
235 xsltLocaleMutex = NULL;
236 #endif
237 initialized = 0;
241 * xsltIsBlank:
242 * @str: a string
244 * Check if a string is ignorable
246 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
249 xsltIsBlank(xmlChar *str) {
250 if (str == NULL)
251 return(1);
252 while (*str != 0) {
253 if (!(IS_BLANK(*str))) return(0);
254 str++;
256 return(1);
259 /************************************************************************
261 * Routines to handle XSLT data structures *
263 ************************************************************************/
264 static xsltDecimalFormatPtr
265 xsltNewDecimalFormat(const xmlChar *nsUri, xmlChar *name)
267 xsltDecimalFormatPtr self;
268 /* UTF-8 for 0x2030 */
269 static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0};
271 self = xmlMalloc(sizeof(xsltDecimalFormat));
272 if (self != NULL) {
273 self->next = NULL;
274 self->nsUri = nsUri;
275 self->name = name;
277 /* Default values */
278 self->digit = xmlStrdup(BAD_CAST("#"));
279 self->patternSeparator = xmlStrdup(BAD_CAST(";"));
280 self->decimalPoint = xmlStrdup(BAD_CAST("."));
281 self->grouping = xmlStrdup(BAD_CAST(","));
282 self->percent = xmlStrdup(BAD_CAST("%"));
283 self->permille = xmlStrdup(BAD_CAST(permille));
284 self->zeroDigit = xmlStrdup(BAD_CAST("0"));
285 self->minusSign = xmlStrdup(BAD_CAST("-"));
286 self->infinity = xmlStrdup(BAD_CAST("Infinity"));
287 self->noNumber = xmlStrdup(BAD_CAST("NaN"));
289 return self;
292 static void
293 xsltFreeDecimalFormat(xsltDecimalFormatPtr self)
295 if (self != NULL) {
296 if (self->digit)
297 xmlFree(self->digit);
298 if (self->patternSeparator)
299 xmlFree(self->patternSeparator);
300 if (self->decimalPoint)
301 xmlFree(self->decimalPoint);
302 if (self->grouping)
303 xmlFree(self->grouping);
304 if (self->percent)
305 xmlFree(self->percent);
306 if (self->permille)
307 xmlFree(self->permille);
308 if (self->zeroDigit)
309 xmlFree(self->zeroDigit);
310 if (self->minusSign)
311 xmlFree(self->minusSign);
312 if (self->infinity)
313 xmlFree(self->infinity);
314 if (self->noNumber)
315 xmlFree(self->noNumber);
316 if (self->name)
317 xmlFree(self->name);
318 xmlFree(self);
322 static void
323 xsltFreeDecimalFormatList(xsltStylesheetPtr self)
325 xsltDecimalFormatPtr iter;
326 xsltDecimalFormatPtr tmp;
328 if (self == NULL)
329 return;
331 iter = self->decimalFormat;
332 while (iter != NULL) {
333 tmp = iter->next;
334 xsltFreeDecimalFormat(iter);
335 iter = tmp;
340 * xsltDecimalFormatGetByName:
341 * @style: the XSLT stylesheet
342 * @name: the decimal-format name to find
344 * Find decimal-format by name
346 * Returns the xsltDecimalFormatPtr
348 xsltDecimalFormatPtr
349 xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name)
351 xsltDecimalFormatPtr result = NULL;
353 if (name == NULL)
354 return style->decimalFormat;
356 while (style != NULL) {
357 for (result = style->decimalFormat->next;
358 result != NULL;
359 result = result->next) {
360 if ((result->nsUri == NULL) && xmlStrEqual(name, result->name))
361 return result;
363 style = xsltNextImport(style);
365 return result;
369 * xsltDecimalFormatGetByQName:
370 * @style: the XSLT stylesheet
371 * @nsUri: the namespace URI of the QName
372 * @name: the local part of the QName
374 * Find decimal-format by QName
376 * Returns the xsltDecimalFormatPtr
378 xsltDecimalFormatPtr
379 xsltDecimalFormatGetByQName(xsltStylesheetPtr style, const xmlChar *nsUri,
380 const xmlChar *name)
382 xsltDecimalFormatPtr result = NULL;
384 if (name == NULL)
385 return style->decimalFormat;
387 while (style != NULL) {
388 for (result = style->decimalFormat->next;
389 result != NULL;
390 result = result->next) {
391 if (xmlStrEqual(nsUri, result->nsUri) &&
392 xmlStrEqual(name, result->name))
393 return result;
395 style = xsltNextImport(style);
397 return result;
402 * xsltNewTemplate:
404 * Create a new XSLT Template
406 * Returns the newly allocated xsltTemplatePtr or NULL in case of error
408 static xsltTemplatePtr
409 xsltNewTemplate(void) {
410 xsltTemplatePtr cur;
412 cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate));
413 if (cur == NULL) {
414 xsltTransformError(NULL, NULL, NULL,
415 "xsltNewTemplate : malloc failed\n");
416 return(NULL);
418 memset(cur, 0, sizeof(xsltTemplate));
419 cur->priority = XSLT_PAT_NO_PRIORITY;
420 return(cur);
424 * xsltFreeTemplate:
425 * @template: an XSLT template
427 * Free up the memory allocated by @template
429 static void
430 xsltFreeTemplate(xsltTemplatePtr template) {
431 if (template == NULL)
432 return;
433 if (template->match) xmlFree(template->match);
435 * NOTE: @name and @nameURI are put into the string dict now.
436 * if (template->name) xmlFree(template->name);
437 * if (template->nameURI) xmlFree(template->nameURI);
440 if (template->mode) xmlFree(template->mode);
441 if (template->modeURI) xmlFree(template->modeURI);
443 if (template->inheritedNs) xmlFree(template->inheritedNs);
445 /* free profiling data */
446 if (template->templCalledTab) xmlFree(template->templCalledTab);
447 if (template->templCountTab) xmlFree(template->templCountTab);
449 memset(template, -1, sizeof(xsltTemplate));
450 xmlFree(template);
454 * xsltFreeTemplateList:
455 * @template: an XSLT template list
457 * Free up the memory allocated by all the elements of @template
459 static void
460 xsltFreeTemplateList(xsltTemplatePtr template) {
461 xsltTemplatePtr cur;
463 while (template != NULL) {
464 cur = template;
465 template = template->next;
466 xsltFreeTemplate(cur);
470 #ifdef XSLT_REFACTORED
472 static void
473 xsltFreeNsAliasList(xsltNsAliasPtr item)
475 xsltNsAliasPtr tmp;
477 while (item) {
478 tmp = item;
479 item = item->next;
480 xmlFree(tmp);
482 return;
485 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
486 static void
487 xsltFreeNamespaceMap(xsltNsMapPtr item)
489 xsltNsMapPtr tmp;
491 while (item) {
492 tmp = item;
493 item = item->next;
494 xmlFree(tmp);
496 return;
499 static xsltNsMapPtr
500 xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt,
501 xmlDocPtr doc,
502 xmlNsPtr ns,
503 xmlNodePtr elem)
505 xsltNsMapPtr ret;
507 if ((cctxt == NULL) || (doc == NULL) || (ns == NULL))
508 return(NULL);
510 ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap));
511 if (ret == NULL) {
512 xsltTransformError(NULL, cctxt->style, elem,
513 "Internal error: (xsltNewNamespaceMapItem) "
514 "memory allocation failed.\n");
515 return(NULL);
517 memset(ret, 0, sizeof(xsltNsMap));
518 ret->doc = doc;
519 ret->ns = ns;
520 ret->origNsName = ns->href;
522 * Store the item at current stylesheet-level.
524 if (cctxt->psData->nsMap != NULL)
525 ret->next = cctxt->psData->nsMap;
526 cctxt->psData->nsMap = ret;
528 return(ret);
530 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
533 * xsltCompilerVarInfoFree:
534 * @cctxt: the compilation context
536 * Frees the list of information for vars/params.
538 static void
539 xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt)
541 xsltVarInfoPtr ivar = cctxt->ivars, ivartmp;
543 while (ivar) {
544 ivartmp = ivar;
545 ivar = ivar->next;
546 xmlFree(ivartmp);
551 * xsltCompilerCtxtFree:
553 * Free an XSLT compiler context.
555 static void
556 xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
558 if (cctxt == NULL)
559 return;
560 #ifdef WITH_XSLT_DEBUG_PARSING
561 xsltGenericDebug(xsltGenericDebugContext,
562 "Freeing compilation context\n");
563 xsltGenericDebug(xsltGenericDebugContext,
564 "### Max inodes: %d\n", cctxt->maxNodeInfos);
565 xsltGenericDebug(xsltGenericDebugContext,
566 "### Max LREs : %d\n", cctxt->maxLREs);
567 #endif
569 * Free node-infos.
571 if (cctxt->inodeList != NULL) {
572 xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList;
573 while (cur != NULL) {
574 tmp = cur;
575 cur = cur->next;
576 xmlFree(tmp);
579 if (cctxt->tmpList != NULL)
580 xsltPointerListFree(cctxt->tmpList);
581 if (cctxt->nsAliases != NULL)
582 xsltFreeNsAliasList(cctxt->nsAliases);
584 if (cctxt->ivars)
585 xsltCompilerVarInfoFree(cctxt);
587 xmlFree(cctxt);
591 * xsltCompilerCreate:
593 * Creates an XSLT compiler context.
595 * Returns the pointer to the created xsltCompilerCtxt or
596 * NULL in case of an internal error.
598 static xsltCompilerCtxtPtr
599 xsltCompilationCtxtCreate(xsltStylesheetPtr style) {
600 xsltCompilerCtxtPtr ret;
602 ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt));
603 if (ret == NULL) {
604 xsltTransformError(NULL, style, NULL,
605 "xsltCompilerCreate: allocation of compiler "
606 "context failed.\n");
607 return(NULL);
609 memset(ret, 0, sizeof(xsltCompilerCtxt));
611 ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR;
612 ret->tmpList = xsltPointerListCreate(20);
613 if (ret->tmpList == NULL) {
614 goto internal_err;
617 return(ret);
619 internal_err:
620 xsltCompilationCtxtFree(ret);
621 return(NULL);
624 static void
625 xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first)
627 xsltEffectiveNsPtr tmp;
629 while (first != NULL) {
630 tmp = first;
631 first = first->nextInStore;
632 xmlFree(tmp);
636 static void
637 xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data)
639 if (data == NULL)
640 return;
642 if (data->inScopeNamespaces != NULL) {
643 int i;
644 xsltNsListContainerPtr nsi;
645 xsltPointerListPtr list =
646 (xsltPointerListPtr) data->inScopeNamespaces;
648 for (i = 0; i < list->number; i++) {
650 * REVISIT TODO: Free info of in-scope namespaces.
652 nsi = (xsltNsListContainerPtr) list->items[i];
653 if (nsi->list != NULL)
654 xmlFree(nsi->list);
655 xmlFree(nsi);
657 xsltPointerListFree(list);
658 data->inScopeNamespaces = NULL;
661 if (data->exclResultNamespaces != NULL) {
662 int i;
663 xsltPointerListPtr list = (xsltPointerListPtr)
664 data->exclResultNamespaces;
666 for (i = 0; i < list->number; i++)
667 xsltPointerListFree((xsltPointerListPtr) list->items[i]);
669 xsltPointerListFree(list);
670 data->exclResultNamespaces = NULL;
673 if (data->extElemNamespaces != NULL) {
674 xsltPointerListPtr list = (xsltPointerListPtr)
675 data->extElemNamespaces;
676 int i;
678 for (i = 0; i < list->number; i++)
679 xsltPointerListFree((xsltPointerListPtr) list->items[i]);
681 xsltPointerListFree(list);
682 data->extElemNamespaces = NULL;
684 if (data->effectiveNs) {
685 xsltLREEffectiveNsNodesFree(data->effectiveNs);
686 data->effectiveNs = NULL;
688 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
689 xsltFreeNamespaceMap(data->nsMap);
690 #endif
691 xmlFree(data);
694 static xsltPrincipalStylesheetDataPtr
695 xsltNewPrincipalStylesheetData(void)
697 xsltPrincipalStylesheetDataPtr ret;
699 ret = (xsltPrincipalStylesheetDataPtr)
700 xmlMalloc(sizeof(xsltPrincipalStylesheetData));
701 if (ret == NULL) {
702 xsltTransformError(NULL, NULL, NULL,
703 "xsltNewPrincipalStylesheetData: memory allocation failed.\n");
704 return(NULL);
706 memset(ret, 0, sizeof(xsltPrincipalStylesheetData));
709 * Global list of in-scope namespaces.
711 ret->inScopeNamespaces = xsltPointerListCreate(-1);
712 if (ret->inScopeNamespaces == NULL)
713 goto internal_err;
715 * Global list of excluded result ns-decls.
717 ret->exclResultNamespaces = xsltPointerListCreate(-1);
718 if (ret->exclResultNamespaces == NULL)
719 goto internal_err;
721 * Global list of extension instruction namespace names.
723 ret->extElemNamespaces = xsltPointerListCreate(-1);
724 if (ret->extElemNamespaces == NULL)
725 goto internal_err;
727 return(ret);
729 internal_err:
731 return(NULL);
734 #endif
737 * xsltNewStylesheetInternal:
738 * @parent: the parent stylesheet or NULL
740 * Create a new XSLT Stylesheet
742 * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
744 static xsltStylesheetPtr
745 xsltNewStylesheetInternal(xsltStylesheetPtr parent) {
746 xsltStylesheetPtr ret = NULL;
748 ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet));
749 if (ret == NULL) {
750 xsltTransformError(NULL, NULL, NULL,
751 "xsltNewStylesheet : malloc failed\n");
752 goto internal_err;
754 memset(ret, 0, sizeof(xsltStylesheet));
756 ret->parent = parent;
757 ret->omitXmlDeclaration = -1;
758 ret->standalone = -1;
759 ret->decimalFormat = xsltNewDecimalFormat(NULL, NULL);
760 ret->indent = -1;
761 ret->errors = 0;
762 ret->warnings = 0;
763 ret->exclPrefixNr = 0;
764 ret->exclPrefixMax = 0;
765 ret->exclPrefixTab = NULL;
766 ret->extInfos = NULL;
767 ret->extrasNr = 0;
768 ret->internalized = 1;
769 ret->literal_result = 0;
770 ret->forwards_compatible = 0;
771 ret->dict = xmlDictCreate();
772 #ifdef WITH_XSLT_DEBUG
773 xsltGenericDebug(xsltGenericDebugContext,
774 "creating dictionary for stylesheet\n");
775 #endif
777 if (parent == NULL) {
778 ret->principal = ret;
780 ret->xpathCtxt = xmlXPathNewContext(NULL);
781 if (ret->xpathCtxt == NULL) {
782 xsltTransformError(NULL, NULL, NULL,
783 "xsltNewStylesheet: xmlXPathNewContext failed\n");
784 goto internal_err;
786 if (xmlXPathContextSetCache(ret->xpathCtxt, 1, -1, 0) == -1)
787 goto internal_err;
788 } else {
789 ret->principal = parent->principal;
792 xsltInit();
794 return(ret);
796 internal_err:
797 if (ret != NULL)
798 xsltFreeStylesheet(ret);
799 return(NULL);
803 * xsltNewStylesheet:
805 * Create a new XSLT Stylesheet
807 * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
809 xsltStylesheetPtr
810 xsltNewStylesheet(void) {
811 return xsltNewStylesheetInternal(NULL);
815 * xsltAllocateExtra:
816 * @style: an XSLT stylesheet
818 * Allocate an extra runtime information slot statically while compiling
819 * the stylesheet and return its number
821 * Returns the number of the slot
824 xsltAllocateExtra(xsltStylesheetPtr style)
826 return(style->extrasNr++);
830 * xsltAllocateExtraCtxt:
831 * @ctxt: an XSLT transformation context
833 * Allocate an extra runtime information slot at run-time
834 * and return its number
835 * This make sure there is a slot ready in the transformation context
837 * Returns the number of the slot
840 xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt)
842 if (ctxt->extrasNr >= ctxt->extrasMax) {
843 int i;
844 if (ctxt->extrasNr == 0) {
845 ctxt->extrasMax = 20;
846 ctxt->extras = (xsltRuntimeExtraPtr)
847 xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra));
848 if (ctxt->extras == NULL) {
849 xsltTransformError(ctxt, NULL, NULL,
850 "xsltAllocateExtraCtxt: out of memory\n");
851 return(0);
853 for (i = 0;i < ctxt->extrasMax;i++) {
854 ctxt->extras[i].info = NULL;
855 ctxt->extras[i].deallocate = NULL;
856 ctxt->extras[i].val.ptr = NULL;
859 } else {
860 xsltRuntimeExtraPtr tmp;
862 ctxt->extrasMax += 100;
863 tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras,
864 ctxt->extrasMax * sizeof(xsltRuntimeExtra));
865 if (tmp == NULL) {
866 xsltTransformError(ctxt, NULL, NULL,
867 "xsltAllocateExtraCtxt: out of memory\n");
868 return(0);
870 ctxt->extras = tmp;
871 for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) {
872 ctxt->extras[i].info = NULL;
873 ctxt->extras[i].deallocate = NULL;
874 ctxt->extras[i].val.ptr = NULL;
878 return(ctxt->extrasNr++);
882 * xsltFreeStylesheetList:
883 * @style: an XSLT stylesheet list
885 * Free up the memory allocated by the list @style
887 static void
888 xsltFreeStylesheetList(xsltStylesheetPtr style) {
889 xsltStylesheetPtr next;
891 while (style != NULL) {
892 next = style->next;
893 xsltFreeStylesheet(style);
894 style = next;
899 * xsltCleanupStylesheetTree:
901 * @doc: the document-node
902 * @node: the element where the stylesheet is rooted at
904 * Actually @node need not be the document-element, but
905 * currently Libxslt does not support embedded stylesheets.
907 * Returns 0 if OK, -1 on API or internal errors.
909 static int
910 xsltCleanupStylesheetTree(xmlDocPtr doc ATTRIBUTE_UNUSED,
911 xmlNodePtr rootElem ATTRIBUTE_UNUSED)
913 #if 0 /* TODO: Currently disabled, since probably not needed. */
914 xmlNodePtr cur;
916 if ((doc == NULL) || (rootElem == NULL) ||
917 (rootElem->type != XML_ELEMENT_NODE) ||
918 (doc != rootElem->doc))
919 return(-1);
922 * Cleanup was suggested by Aleksey Sanin:
923 * Clear the PSVI field to avoid problems if the
924 * node-tree of the stylesheet is intended to be used for
925 * further processing by the user (e.g. for compiling it
926 * once again - although not recommended).
929 cur = rootElem;
930 while (cur != NULL) {
931 if (cur->type == XML_ELEMENT_NODE) {
933 * Clear the PSVI field.
935 cur->psvi = NULL;
936 if (cur->children) {
937 cur = cur->children;
938 continue;
942 leave_node:
943 if (cur == rootElem)
944 break;
945 if (cur->next != NULL)
946 cur = cur->next;
947 else {
948 cur = cur->parent;
949 if (cur == NULL)
950 break;
951 goto leave_node;
954 #endif /* #if 0 */
955 return(0);
959 * xsltFreeStylesheet:
960 * @style: an XSLT stylesheet
962 * Free up the memory allocated by @style
964 void
965 xsltFreeStylesheet(xsltStylesheetPtr style)
967 if (style == NULL)
968 return;
970 #ifdef XSLT_REFACTORED
972 * Start with a cleanup of the main stylesheet's doc.
974 if ((style->principal == style) && (style->doc))
975 xsltCleanupStylesheetTree(style->doc,
976 xmlDocGetRootElement(style->doc));
977 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
979 * Restore changed ns-decls before freeing the document.
981 if ((style->doc != NULL) &&
982 XSLT_HAS_INTERNAL_NSMAP(style))
984 xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style),
985 style->doc);
987 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
988 #else
990 * Start with a cleanup of the main stylesheet's doc.
992 if ((style->parent == NULL) && (style->doc))
993 xsltCleanupStylesheetTree(style->doc,
994 xmlDocGetRootElement(style->doc));
995 #endif /* XSLT_REFACTORED */
997 xsltFreeKeys(style);
998 xsltFreeExts(style);
999 xsltFreeTemplateHashes(style);
1000 xsltFreeDecimalFormatList(style);
1001 xsltFreeTemplateList(style->templates);
1002 xsltFreeAttributeSetsHashes(style);
1003 xsltFreeNamespaceAliasHashes(style);
1004 xsltFreeStylePreComps(style);
1006 * Free documents of all included stylsheet modules of this
1007 * stylesheet level.
1009 xsltFreeStyleDocuments(style);
1011 * TODO: Best time to shutdown extension stuff?
1013 xsltShutdownExts(style);
1015 if (style->variables != NULL)
1016 xsltFreeStackElemList(style->variables);
1017 if (style->cdataSection != NULL)
1018 xmlHashFree(style->cdataSection, NULL);
1019 if (style->stripSpaces != NULL)
1020 xmlHashFree(style->stripSpaces, NULL);
1021 if (style->nsHash != NULL)
1022 xmlHashFree(style->nsHash, NULL);
1023 if (style->exclPrefixTab != NULL)
1024 xmlFree(style->exclPrefixTab);
1025 if (style->method != NULL)
1026 xmlFree(style->method);
1027 if (style->methodURI != NULL)
1028 xmlFree(style->methodURI);
1029 if (style->version != NULL)
1030 xmlFree(style->version);
1031 if (style->encoding != NULL)
1032 xmlFree(style->encoding);
1033 if (style->doctypePublic != NULL)
1034 xmlFree(style->doctypePublic);
1035 if (style->doctypeSystem != NULL)
1036 xmlFree(style->doctypeSystem);
1037 if (style->mediaType != NULL)
1038 xmlFree(style->mediaType);
1039 if (style->attVTs)
1040 xsltFreeAVTList(style->attVTs);
1041 if (style->imports != NULL)
1042 xsltFreeStylesheetList(style->imports);
1044 #ifdef XSLT_REFACTORED
1046 * If this is the principal stylesheet, then
1047 * free its internal data.
1049 if (style->principal == style) {
1050 if (style->principalData) {
1051 xsltFreePrincipalStylesheetData(style->principalData);
1052 style->principalData = NULL;
1055 #endif
1057 * Better to free the main document of this stylesheet level
1058 * at the end - so here.
1060 if (style->doc != NULL) {
1061 xmlFreeDoc(style->doc);
1064 #ifdef WITH_XSLT_DEBUG
1065 xsltGenericDebug(xsltGenericDebugContext,
1066 "freeing dictionary from stylesheet\n");
1067 #endif
1068 xmlDictFree(style->dict);
1070 if (style->xpathCtxt != NULL)
1071 xmlXPathFreeContext(style->xpathCtxt);
1073 memset(style, -1, sizeof(xsltStylesheet));
1074 xmlFree(style);
1077 /************************************************************************
1079 * Parsing of an XSLT Stylesheet *
1081 ************************************************************************/
1083 #ifdef XSLT_REFACTORED
1085 * This is now performed in an optimized way in xsltParseXSLTTemplate.
1087 #else
1089 * xsltGetInheritedNsList:
1090 * @style: the stylesheet
1091 * @template: the template
1092 * @node: the current node
1094 * Search all the namespace applying to a given element except the ones
1095 * from excluded output prefixes currently in scope. Initialize the
1096 * template inheritedNs list with it.
1098 * Returns the number of entries found
1100 static int
1101 xsltGetInheritedNsList(xsltStylesheetPtr style,
1102 xsltTemplatePtr template,
1103 xmlNodePtr node)
1105 xmlNsPtr cur;
1106 xmlNsPtr *ret = NULL, *tmp;
1107 int nbns = 0;
1108 int maxns = 0;
1109 int i;
1111 if ((style == NULL) || (template == NULL) || (node == NULL) ||
1112 (template->inheritedNsNr != 0) || (template->inheritedNs != NULL))
1113 return(0);
1114 while (node != NULL) {
1115 if (node->type == XML_ELEMENT_NODE) {
1116 cur = node->nsDef;
1117 while (cur != NULL) {
1118 if (xmlStrEqual(cur->href, XSLT_NAMESPACE))
1119 goto skip_ns;
1121 if ((cur->prefix != NULL) &&
1122 (xsltCheckExtPrefix(style, cur->prefix)))
1123 goto skip_ns;
1125 * Check if this namespace was excluded.
1126 * Note that at this point only the exclusions defined
1127 * on the topmost stylesheet element are in the exclusion-list.
1129 for (i = 0;i < style->exclPrefixNr;i++) {
1130 if (xmlStrEqual(cur->href, style->exclPrefixTab[i]))
1131 goto skip_ns;
1134 * Skip shadowed namespace bindings.
1136 for (i = 0; i < nbns; i++) {
1137 if ((cur->prefix == ret[i]->prefix) ||
1138 (xmlStrEqual(cur->prefix, ret[i]->prefix)))
1139 break;
1141 if (i >= nbns) {
1142 if (nbns >= maxns) {
1143 maxns = (maxns == 0) ? 10 : 2 * maxns;
1144 tmp = (xmlNsPtr *) xmlRealloc(ret,
1145 (maxns + 1) * sizeof(xmlNsPtr));
1146 if (tmp == NULL) {
1147 xmlGenericError(xmlGenericErrorContext,
1148 "xsltGetInheritedNsList : realloc failed!\n");
1149 xmlFree(ret);
1150 return(0);
1152 ret = tmp;
1154 ret[nbns++] = cur;
1155 ret[nbns] = NULL;
1157 skip_ns:
1158 cur = cur->next;
1161 node = node->parent;
1163 if (nbns != 0) {
1164 #ifdef WITH_XSLT_DEBUG_PARSING
1165 xsltGenericDebug(xsltGenericDebugContext,
1166 "template has %d inherited namespaces\n", nbns);
1167 #endif
1168 template->inheritedNsNr = nbns;
1169 template->inheritedNs = ret;
1171 return (nbns);
1173 #endif /* else of XSLT_REFACTORED */
1176 * xsltParseStylesheetOutput:
1177 * @style: the XSLT stylesheet
1178 * @cur: the "output" element
1180 * parse an XSLT stylesheet output element and record
1181 * information related to the stylesheet output
1184 void
1185 xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur)
1187 xmlChar *elements,
1188 *prop;
1189 xmlChar *element,
1190 *end;
1192 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1193 return;
1195 prop = xmlGetNsProp(cur, (const xmlChar *) "version", NULL);
1196 if (prop != NULL) {
1197 if (style->version != NULL)
1198 xmlFree(style->version);
1199 style->version = prop;
1202 prop = xmlGetNsProp(cur, (const xmlChar *) "encoding", NULL);
1203 if (prop != NULL) {
1204 if (style->encoding != NULL)
1205 xmlFree(style->encoding);
1206 style->encoding = prop;
1209 /* relaxed to support xt:document
1210 * TODO KB: What does "relaxed to support xt:document" mean?
1212 prop = xmlGetNsProp(cur, (const xmlChar *) "method", NULL);
1213 if (prop != NULL) {
1214 const xmlChar *URI;
1216 if (style->method != NULL)
1217 xmlFree(style->method);
1218 style->method = NULL;
1219 if (style->methodURI != NULL)
1220 xmlFree(style->methodURI);
1221 style->methodURI = NULL;
1224 * TODO: Don't use xsltGetQNameURI().
1226 URI = xsltGetQNameURI(cur, &prop);
1227 if (prop == NULL) {
1228 if (style != NULL) style->errors++;
1229 } else if (URI == NULL) {
1230 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
1231 (xmlStrEqual(prop, (const xmlChar *) "html")) ||
1232 (xmlStrEqual(prop, (const xmlChar *) "text"))) {
1233 style->method = prop;
1234 } else {
1235 xsltTransformError(NULL, style, cur,
1236 "invalid value for method: %s\n", prop);
1237 if (style != NULL) style->warnings++;
1238 xmlFree(prop);
1240 } else {
1241 style->method = prop;
1242 style->methodURI = xmlStrdup(URI);
1246 prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-system", NULL);
1247 if (prop != NULL) {
1248 if (style->doctypeSystem != NULL)
1249 xmlFree(style->doctypeSystem);
1250 style->doctypeSystem = prop;
1253 prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-public", NULL);
1254 if (prop != NULL) {
1255 if (style->doctypePublic != NULL)
1256 xmlFree(style->doctypePublic);
1257 style->doctypePublic = prop;
1260 prop = xmlGetNsProp(cur, (const xmlChar *) "standalone", NULL);
1261 if (prop != NULL) {
1262 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1263 style->standalone = 1;
1264 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1265 style->standalone = 0;
1266 } else {
1267 xsltTransformError(NULL, style, cur,
1268 "invalid value for standalone: %s\n", prop);
1269 style->errors++;
1271 xmlFree(prop);
1274 prop = xmlGetNsProp(cur, (const xmlChar *) "indent", NULL);
1275 if (prop != NULL) {
1276 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1277 style->indent = 1;
1278 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1279 style->indent = 0;
1280 } else {
1281 xsltTransformError(NULL, style, cur,
1282 "invalid value for indent: %s\n", prop);
1283 style->errors++;
1285 xmlFree(prop);
1288 prop = xmlGetNsProp(cur, (const xmlChar *) "omit-xml-declaration", NULL);
1289 if (prop != NULL) {
1290 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1291 style->omitXmlDeclaration = 1;
1292 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1293 style->omitXmlDeclaration = 0;
1294 } else {
1295 xsltTransformError(NULL, style, cur,
1296 "invalid value for omit-xml-declaration: %s\n",
1297 prop);
1298 style->errors++;
1300 xmlFree(prop);
1303 elements = xmlGetNsProp(cur, (const xmlChar *) "cdata-section-elements",
1304 NULL);
1305 if (elements != NULL) {
1306 if (style->cdataSection == NULL)
1307 style->cdataSection = xmlHashCreate(10);
1308 if (style->cdataSection == NULL) {
1309 xmlFree(elements);
1310 return;
1313 element = elements;
1314 while (*element != 0) {
1315 while (IS_BLANK(*element))
1316 element++;
1317 if (*element == 0)
1318 break;
1319 end = element;
1320 while ((*end != 0) && (!IS_BLANK(*end)))
1321 end++;
1322 element = xmlStrndup(element, end - element);
1323 if (element) {
1324 #ifdef WITH_XSLT_DEBUG_PARSING
1325 xsltGenericDebug(xsltGenericDebugContext,
1326 "add cdata section output element %s\n",
1327 element);
1328 #endif
1329 if (xmlValidateQName(BAD_CAST element, 0) != 0) {
1330 xsltTransformError(NULL, style, cur,
1331 "Attribute 'cdata-section-elements': The value "
1332 "'%s' is not a valid QName.\n", element);
1333 xmlFree(element);
1334 style->errors++;
1335 } else {
1336 const xmlChar *URI;
1339 * TODO: Don't use xsltGetQNameURI().
1341 URI = xsltGetQNameURI(cur, &element);
1342 if (element == NULL) {
1344 * TODO: We'll report additionally an error
1345 * via the stylesheet's error handling.
1347 xsltTransformError(NULL, style, cur,
1348 "Attribute 'cdata-section-elements': "
1349 "Not a valid QName.\n");
1350 style->errors++;
1351 } else {
1352 xmlNsPtr ns;
1355 * XSLT-1.0 "Each QName is expanded into an
1356 * expanded-name using the namespace declarations in
1357 * effect on the xsl:output element in which the QName
1358 * occurs; if there is a default namespace, it is used
1359 * for QNames that do not have a prefix"
1360 * NOTE: Fix of bug #339570.
1362 if (URI == NULL) {
1363 ns = xmlSearchNs(style->doc, cur, NULL);
1364 if (ns != NULL)
1365 URI = ns->href;
1367 xmlHashAddEntry2(style->cdataSection, element, URI,
1368 (void *) "cdata");
1369 xmlFree(element);
1373 element = end;
1375 xmlFree(elements);
1378 prop = xmlGetNsProp(cur, (const xmlChar *) "media-type", NULL);
1379 if (prop != NULL) {
1380 if (style->mediaType)
1381 xmlFree(style->mediaType);
1382 style->mediaType = prop;
1384 if (cur->children != NULL) {
1385 xsltParseContentError(style, cur->children);
1390 * xsltParseStylesheetDecimalFormat:
1391 * @style: the XSLT stylesheet
1392 * @cur: the "decimal-format" element
1394 * <!-- Category: top-level-element -->
1395 * <xsl:decimal-format
1396 * name = qname, decimal-separator = char, grouping-separator = char,
1397 * infinity = string, minus-sign = char, NaN = string, percent = char
1398 * per-mille = char, zero-digit = char, digit = char,
1399 * pattern-separator = char />
1401 * parse an XSLT stylesheet decimal-format element and
1402 * and record the formatting characteristics
1404 static void
1405 xsltParseStylesheetDecimalFormat(xsltStylesheetPtr style, xmlNodePtr cur)
1407 xmlChar *prop;
1408 xsltDecimalFormatPtr format;
1409 xsltDecimalFormatPtr iter;
1411 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1412 return;
1414 format = style->decimalFormat;
1416 prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL);
1417 if (prop != NULL) {
1418 const xmlChar *nsUri;
1420 if (xmlValidateQName(prop, 0) != 0) {
1421 xsltTransformError(NULL, style, cur,
1422 "xsl:decimal-format: Invalid QName '%s'.\n", prop);
1423 style->warnings++;
1424 xmlFree(prop);
1425 return;
1428 * TODO: Don't use xsltGetQNameURI().
1430 nsUri = xsltGetQNameURI(cur, &prop);
1431 if (prop == NULL) {
1432 style->warnings++;
1433 return;
1435 format = xsltDecimalFormatGetByQName(style, nsUri, prop);
1436 if (format != NULL) {
1437 xsltTransformError(NULL, style, cur,
1438 "xsltParseStylestyleDecimalFormat: %s already exists\n", prop);
1439 style->warnings++;
1440 xmlFree(prop);
1441 return;
1443 format = xsltNewDecimalFormat(nsUri, prop);
1444 if (format == NULL) {
1445 xsltTransformError(NULL, style, cur,
1446 "xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n");
1447 style->errors++;
1448 xmlFree(prop);
1449 return;
1451 /* Append new decimal-format structure */
1452 for (iter = style->decimalFormat; iter->next; iter = iter->next)
1454 if (iter)
1455 iter->next = format;
1458 prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL);
1459 if (prop != NULL) {
1460 if (format->decimalPoint != NULL) xmlFree(format->decimalPoint);
1461 format->decimalPoint = prop;
1464 prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL);
1465 if (prop != NULL) {
1466 if (format->grouping != NULL) xmlFree(format->grouping);
1467 format->grouping = prop;
1470 prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL);
1471 if (prop != NULL) {
1472 if (format->infinity != NULL) xmlFree(format->infinity);
1473 format->infinity = prop;
1476 prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL);
1477 if (prop != NULL) {
1478 if (format->minusSign != NULL) xmlFree(format->minusSign);
1479 format->minusSign = prop;
1482 prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL);
1483 if (prop != NULL) {
1484 if (format->noNumber != NULL) xmlFree(format->noNumber);
1485 format->noNumber = prop;
1488 prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL);
1489 if (prop != NULL) {
1490 if (format->percent != NULL) xmlFree(format->percent);
1491 format->percent = prop;
1494 prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL);
1495 if (prop != NULL) {
1496 if (format->permille != NULL) xmlFree(format->permille);
1497 format->permille = prop;
1500 prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL);
1501 if (prop != NULL) {
1502 if (format->zeroDigit != NULL) xmlFree(format->zeroDigit);
1503 format->zeroDigit = prop;
1506 prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL);
1507 if (prop != NULL) {
1508 if (format->digit != NULL) xmlFree(format->digit);
1509 format->digit = prop;
1512 prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL);
1513 if (prop != NULL) {
1514 if (format->patternSeparator != NULL) xmlFree(format->patternSeparator);
1515 format->patternSeparator = prop;
1517 if (cur->children != NULL) {
1518 xsltParseContentError(style, cur->children);
1523 * xsltParseStylesheetPreserveSpace:
1524 * @style: the XSLT stylesheet
1525 * @cur: the "preserve-space" element
1527 * parse an XSLT stylesheet preserve-space element and record
1528 * elements needing preserving
1531 static void
1532 xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
1533 xmlChar *elements;
1534 xmlChar *element, *end;
1536 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1537 return;
1539 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
1540 if (elements == NULL) {
1541 xsltTransformError(NULL, style, cur,
1542 "xsltParseStylesheetPreserveSpace: missing elements attribute\n");
1543 if (style != NULL) style->warnings++;
1544 return;
1547 if (style->stripSpaces == NULL)
1548 style->stripSpaces = xmlHashCreate(10);
1549 if (style->stripSpaces == NULL) {
1550 xmlFree(elements);
1551 return;
1554 element = elements;
1555 while (*element != 0) {
1556 while (IS_BLANK(*element)) element++;
1557 if (*element == 0)
1558 break;
1559 end = element;
1560 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1561 element = xmlStrndup(element, end - element);
1562 if (element) {
1563 #ifdef WITH_XSLT_DEBUG_PARSING
1564 xsltGenericDebug(xsltGenericDebugContext,
1565 "add preserved space element %s\n", element);
1566 #endif
1567 if (xmlStrEqual(element, (const xmlChar *)"*")) {
1568 style->stripAll = -1;
1569 } else {
1570 const xmlChar *URI;
1573 * TODO: Don't use xsltGetQNameURI().
1575 URI = xsltGetQNameURI(cur, &element);
1577 xmlHashAddEntry2(style->stripSpaces, element, URI,
1578 (xmlChar *) "preserve");
1580 xmlFree(element);
1582 element = end;
1584 xmlFree(elements);
1585 if (cur->children != NULL) {
1586 xsltParseContentError(style, cur->children);
1590 #ifdef XSLT_REFACTORED
1591 #else
1593 * xsltParseStylesheetExtPrefix:
1594 * @style: the XSLT stylesheet
1595 * @template: the "extension-element-prefixes" prefix
1597 * parse an XSLT stylesheet's "extension-element-prefix" attribute value
1598 * and register the namespaces of extension instruction.
1599 * SPEC "A namespace is designated as an extension namespace by using
1600 * an extension-element-prefixes attribute on:
1601 * 1) an xsl:stylesheet element
1602 * 2) an xsl:extension-element-prefixes attribute on a
1603 * literal result element
1604 * 3) an extension instruction."
1606 static void
1607 xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur,
1608 int isXsltElem) {
1609 xmlChar *prefixes;
1610 xmlChar *prefix, *end;
1612 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1613 return;
1615 if (isXsltElem) {
1616 /* For xsl:stylesheet/xsl:transform. */
1617 prefixes = xmlGetNsProp(cur,
1618 (const xmlChar *)"extension-element-prefixes", NULL);
1619 } else {
1620 /* For literal result elements and extension instructions. */
1621 prefixes = xmlGetNsProp(cur,
1622 (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE);
1624 if (prefixes == NULL) {
1625 return;
1628 prefix = prefixes;
1629 while (*prefix != 0) {
1630 while (IS_BLANK(*prefix)) prefix++;
1631 if (*prefix == 0)
1632 break;
1633 end = prefix;
1634 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1635 prefix = xmlStrndup(prefix, end - prefix);
1636 if (prefix) {
1637 xmlNsPtr ns;
1639 if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
1640 ns = xmlSearchNs(style->doc, cur, NULL);
1641 else
1642 ns = xmlSearchNs(style->doc, cur, prefix);
1643 if (ns == NULL) {
1644 xsltTransformError(NULL, style, cur,
1645 "xsl:extension-element-prefix : undefined namespace %s\n",
1646 prefix);
1647 if (style != NULL) style->warnings++;
1648 } else {
1649 #ifdef WITH_XSLT_DEBUG_PARSING
1650 xsltGenericDebug(xsltGenericDebugContext,
1651 "add extension prefix %s\n", prefix);
1652 #endif
1653 xsltRegisterExtPrefix(style, prefix, ns->href);
1655 xmlFree(prefix);
1657 prefix = end;
1659 xmlFree(prefixes);
1661 #endif /* else of XSLT_REFACTORED */
1664 * xsltParseStylesheetStripSpace:
1665 * @style: the XSLT stylesheet
1666 * @cur: the "strip-space" element
1668 * parse an XSLT stylesheet's strip-space element and record
1669 * the elements needing stripping
1672 static void
1673 xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
1674 xmlChar *elements;
1675 xmlChar *element, *end;
1677 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1678 return;
1680 if (style->stripSpaces == NULL)
1681 style->stripSpaces = xmlHashCreate(10);
1682 if (style->stripSpaces == NULL)
1683 return;
1685 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
1686 if (elements == NULL) {
1687 xsltTransformError(NULL, style, cur,
1688 "xsltParseStylesheetStripSpace: missing elements attribute\n");
1689 if (style != NULL) style->warnings++;
1690 return;
1693 element = elements;
1694 while (*element != 0) {
1695 while (IS_BLANK(*element)) element++;
1696 if (*element == 0)
1697 break;
1698 end = element;
1699 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1700 element = xmlStrndup(element, end - element);
1701 if (element) {
1702 #ifdef WITH_XSLT_DEBUG_PARSING
1703 xsltGenericDebug(xsltGenericDebugContext,
1704 "add stripped space element %s\n", element);
1705 #endif
1706 if (xmlStrEqual(element, (const xmlChar *)"*")) {
1707 style->stripAll = 1;
1708 } else {
1709 const xmlChar *URI;
1712 * TODO: Don't use xsltGetQNameURI().
1714 URI = xsltGetQNameURI(cur, &element);
1716 xmlHashAddEntry2(style->stripSpaces, element, URI,
1717 (xmlChar *) "strip");
1719 xmlFree(element);
1721 element = end;
1723 xmlFree(elements);
1724 if (cur->children != NULL) {
1725 xsltParseContentError(style, cur->children);
1729 #ifdef XSLT_REFACTORED
1730 #else
1732 * xsltParseStylesheetExcludePrefix:
1733 * @style: the XSLT stylesheet
1734 * @cur: the current point in the stylesheet
1736 * parse an XSLT stylesheet exclude prefix and record
1737 * namespaces needing stripping
1739 * Returns the number of Excluded prefixes added at that level
1742 static int
1743 xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur,
1744 int isXsltElem)
1746 int nb = 0;
1747 xmlChar *prefixes;
1748 xmlChar *prefix, *end;
1750 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1751 return(0);
1753 if (isXsltElem)
1754 prefixes = xmlGetNsProp(cur,
1755 (const xmlChar *)"exclude-result-prefixes", NULL);
1756 else
1757 prefixes = xmlGetNsProp(cur,
1758 (const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE);
1760 if (prefixes == NULL) {
1761 return(0);
1764 prefix = prefixes;
1765 while (*prefix != 0) {
1766 while (IS_BLANK(*prefix)) prefix++;
1767 if (*prefix == 0)
1768 break;
1769 end = prefix;
1770 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1771 prefix = xmlStrndup(prefix, end - prefix);
1772 if (prefix) {
1773 xmlNsPtr ns;
1775 if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
1776 ns = xmlSearchNs(style->doc, cur, NULL);
1777 else
1778 ns = xmlSearchNs(style->doc, cur, prefix);
1779 if (ns == NULL) {
1780 xsltTransformError(NULL, style, cur,
1781 "xsl:exclude-result-prefixes : undefined namespace %s\n",
1782 prefix);
1783 if (style != NULL) style->warnings++;
1784 } else {
1785 if (exclPrefixPush(style, (xmlChar *) ns->href) >= 0) {
1786 #ifdef WITH_XSLT_DEBUG_PARSING
1787 xsltGenericDebug(xsltGenericDebugContext,
1788 "exclude result prefix %s\n", prefix);
1789 #endif
1790 nb++;
1793 xmlFree(prefix);
1795 prefix = end;
1797 xmlFree(prefixes);
1798 return(nb);
1800 #endif /* else of XSLT_REFACTORED */
1802 #ifdef XSLT_REFACTORED
1805 * xsltTreeEnsureXMLDecl:
1806 * @doc: the doc
1808 * BIG NOTE:
1809 * This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c".
1810 * Ensures that there is an XML namespace declaration on the doc.
1812 * Returns the XML ns-struct or NULL on API and internal errors.
1814 static xmlNsPtr
1815 xsltTreeEnsureXMLDecl(xmlDocPtr doc)
1817 if (doc == NULL)
1818 return (NULL);
1819 if (doc->oldNs != NULL)
1820 return (doc->oldNs);
1822 xmlNsPtr ns;
1823 ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
1824 if (ns == NULL) {
1825 xmlGenericError(xmlGenericErrorContext,
1826 "xsltTreeEnsureXMLDecl: Failed to allocate "
1827 "the XML namespace.\n");
1828 return (NULL);
1830 memset(ns, 0, sizeof(xmlNs));
1831 ns->type = XML_LOCAL_NAMESPACE;
1833 * URGENT TODO: revisit this.
1835 #ifdef LIBXML_NAMESPACE_DICT
1836 if (doc->dict)
1837 ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1);
1838 else
1839 ns->href = xmlStrdup(XML_XML_NAMESPACE);
1840 #else
1841 ns->href = xmlStrdup(XML_XML_NAMESPACE);
1842 #endif
1843 ns->prefix = xmlStrdup((const xmlChar *)"xml");
1844 doc->oldNs = ns;
1845 return (ns);
1850 * xsltTreeAcquireStoredNs:
1851 * @doc: the doc
1852 * @nsName: the namespace name
1853 * @prefix: the prefix
1855 * BIG NOTE:
1856 * This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c".
1857 * Creates or reuses an xmlNs struct on doc->oldNs with
1858 * the given prefix and namespace name.
1860 * Returns the aquired ns struct or NULL in case of an API
1861 * or internal error.
1863 static xmlNsPtr
1864 xsltTreeAcquireStoredNs(xmlDocPtr doc,
1865 const xmlChar *nsName,
1866 const xmlChar *prefix)
1868 xmlNsPtr ns;
1870 if (doc == NULL)
1871 return (NULL);
1872 if (doc->oldNs != NULL)
1873 ns = doc->oldNs;
1874 else
1875 ns = xsltTreeEnsureXMLDecl(doc);
1876 if (ns == NULL)
1877 return (NULL);
1878 if (ns->next != NULL) {
1879 /* Reuse. */
1880 ns = ns->next;
1881 while (ns != NULL) {
1882 if ((ns->prefix == NULL) != (prefix == NULL)) {
1883 /* NOP */
1884 } else if (prefix == NULL) {
1885 if (xmlStrEqual(ns->href, nsName))
1886 return (ns);
1887 } else {
1888 if ((ns->prefix[0] == prefix[0]) &&
1889 xmlStrEqual(ns->prefix, prefix) &&
1890 xmlStrEqual(ns->href, nsName))
1891 return (ns);
1894 if (ns->next == NULL)
1895 break;
1896 ns = ns->next;
1899 /* Create. */
1900 ns->next = xmlNewNs(NULL, nsName, prefix);
1901 return (ns->next);
1905 * xsltLREBuildEffectiveNs:
1907 * Apply ns-aliasing on the namespace of the given @elem and
1908 * its attributes.
1910 static int
1911 xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt,
1912 xmlNodePtr elem)
1914 xmlNsPtr ns;
1915 xsltNsAliasPtr alias;
1917 if ((cctxt == NULL) || (elem == NULL))
1918 return(-1);
1919 if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases))
1920 return(0);
1922 alias = cctxt->nsAliases;
1923 while (alias != NULL) {
1924 if ( /* If both namespaces are NULL... */
1925 ( (elem->ns == NULL) &&
1926 ((alias->literalNs == NULL) ||
1927 (alias->literalNs->href == NULL)) ) ||
1928 /* ... or both namespace are equal */
1929 ( (elem->ns != NULL) &&
1930 (alias->literalNs != NULL) &&
1931 xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
1933 if ((alias->targetNs != NULL) &&
1934 (alias->targetNs->href != NULL))
1937 * Convert namespace.
1939 if (elem->doc == alias->docOfTargetNs) {
1941 * This is the nice case: same docs.
1942 * This will eventually assign a ns-decl which
1943 * is shadowed, but this has no negative effect on
1944 * the generation of the result tree.
1946 elem->ns = alias->targetNs;
1947 } else {
1949 * This target xmlNs originates from a different
1950 * stylesheet tree. Try to locate it in the
1951 * in-scope namespaces.
1952 * OPTIMIZE TODO: Use the compiler-node-info inScopeNs.
1954 ns = xmlSearchNs(elem->doc, elem,
1955 alias->targetNs->prefix);
1957 * If no matching ns-decl found, then assign a
1958 * ns-decl stored in xmlDoc.
1960 if ((ns == NULL) ||
1961 (! xmlStrEqual(ns->href, alias->targetNs->href)))
1964 * BIG NOTE: The use of xsltTreeAcquireStoredNs()
1965 * is not very efficient, but currently I don't
1966 * see an other way of *safely* changing a node's
1967 * namespace, since the xmlNs struct in
1968 * alias->targetNs might come from an other
1969 * stylesheet tree. So we need to anchor it in the
1970 * current document, without adding it to the tree,
1971 * which would otherwise change the in-scope-ns
1972 * semantic of the tree.
1974 ns = xsltTreeAcquireStoredNs(elem->doc,
1975 alias->targetNs->href,
1976 alias->targetNs->prefix);
1978 if (ns == NULL) {
1979 xsltTransformError(NULL, cctxt->style, elem,
1980 "Internal error in "
1981 "xsltLREBuildEffectiveNs(): "
1982 "failed to acquire a stored "
1983 "ns-declaration.\n");
1984 cctxt->style->errors++;
1985 return(-1);
1989 elem->ns = ns;
1991 } else {
1993 * Move into or leave in the NULL namespace.
1995 elem->ns = NULL;
1997 break;
1999 alias = alias->next;
2002 * Same with attributes of literal result elements.
2004 if (elem->properties != NULL) {
2005 xmlAttrPtr attr = elem->properties;
2007 while (attr != NULL) {
2008 if (attr->ns == NULL) {
2009 attr = attr->next;
2010 continue;
2012 alias = cctxt->nsAliases;
2013 while (alias != NULL) {
2014 if ( /* If both namespaces are NULL... */
2015 ( (elem->ns == NULL) &&
2016 ((alias->literalNs == NULL) ||
2017 (alias->literalNs->href == NULL)) ) ||
2018 /* ... or both namespace are equal */
2019 ( (elem->ns != NULL) &&
2020 (alias->literalNs != NULL) &&
2021 xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
2023 if ((alias->targetNs != NULL) &&
2024 (alias->targetNs->href != NULL))
2026 if (elem->doc == alias->docOfTargetNs) {
2027 elem->ns = alias->targetNs;
2028 } else {
2029 ns = xmlSearchNs(elem->doc, elem,
2030 alias->targetNs->prefix);
2031 if ((ns == NULL) ||
2032 (! xmlStrEqual(ns->href, alias->targetNs->href)))
2034 ns = xsltTreeAcquireStoredNs(elem->doc,
2035 alias->targetNs->href,
2036 alias->targetNs->prefix);
2038 if (ns == NULL) {
2039 xsltTransformError(NULL, cctxt->style, elem,
2040 "Internal error in "
2041 "xsltLREBuildEffectiveNs(): "
2042 "failed to acquire a stored "
2043 "ns-declaration.\n");
2044 cctxt->style->errors++;
2045 return(-1);
2049 elem->ns = ns;
2051 } else {
2053 * Move into or leave in the NULL namespace.
2055 elem->ns = NULL;
2057 break;
2059 alias = alias->next;
2062 attr = attr->next;
2065 return(0);
2069 * xsltLREBuildEffectiveNsNodes:
2071 * Computes the effective namespaces nodes for a literal result
2072 * element.
2073 * @effectiveNs is the set of effective ns-nodes
2074 * on the literal result element, which will be added to the result
2075 * element if not already existing in the result tree.
2076 * This means that excluded namespaces (via exclude-result-prefixes,
2077 * extension-element-prefixes and the XSLT namespace) not added
2078 * to the set.
2079 * Namespace-aliasing was applied on the @effectiveNs.
2081 static int
2082 xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
2083 xsltStyleItemLRElementInfoPtr item,
2084 xmlNodePtr elem,
2085 int isLRE)
2087 xmlNsPtr ns, tmpns;
2088 xsltEffectiveNsPtr effNs, lastEffNs = NULL;
2089 int i, j, holdByElem;
2090 xsltPointerListPtr extElemNs = cctxt->inode->extElemNs;
2091 xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs;
2093 if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) ||
2094 (item == NULL) || (item->effectiveNs != NULL))
2095 return(-1);
2097 if (item->inScopeNs == NULL)
2098 return(0);
2100 extElemNs = cctxt->inode->extElemNs;
2101 exclResultNs = cctxt->inode->exclResultNs;
2103 for (i = 0; i < item->inScopeNs->totalNumber; i++) {
2104 ns = item->inScopeNs->list[i];
2106 * Skip namespaces designated as excluded namespaces
2107 * -------------------------------------------------
2109 * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces
2110 * which are target namespaces of namespace-aliases
2111 * regardless if designated as excluded.
2113 * Exclude the XSLT namespace.
2115 if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
2116 goto skip_ns;
2119 * Apply namespace aliasing
2120 * ------------------------
2122 * SPEC XSLT 2.0
2123 * "- A namespace node whose string value is a literal namespace
2124 * URI is not copied to the result tree.
2125 * - A namespace node whose string value is a target namespace URI
2126 * is copied to the result tree, whether or not the URI
2127 * identifies an excluded namespace."
2129 * NOTE: The ns-aliasing machanism is non-cascading.
2130 * (checked with Saxon, Xalan and MSXML .NET).
2131 * URGENT TODO: is style->nsAliases the effective list of
2132 * ns-aliases, or do we need to lookup the whole
2133 * import-tree?
2134 * TODO: Get rid of import-tree lookup.
2136 if (cctxt->hasNsAliases) {
2137 xsltNsAliasPtr alias;
2139 * First check for being a target namespace.
2141 alias = cctxt->nsAliases;
2142 do {
2144 * TODO: Is xmlns="" handled already?
2146 if ((alias->targetNs != NULL) &&
2147 (xmlStrEqual(alias->targetNs->href, ns->href)))
2150 * Recognized as a target namespace; use it regardless
2151 * if excluded otherwise.
2153 goto add_effective_ns;
2155 alias = alias->next;
2156 } while (alias != NULL);
2158 alias = cctxt->nsAliases;
2159 do {
2161 * TODO: Is xmlns="" handled already?
2163 if ((alias->literalNs != NULL) &&
2164 (xmlStrEqual(alias->literalNs->href, ns->href)))
2167 * Recognized as an namespace alias; do not use it.
2169 goto skip_ns;
2171 alias = alias->next;
2172 } while (alias != NULL);
2176 * Exclude excluded result namespaces.
2178 if (exclResultNs) {
2179 for (j = 0; j < exclResultNs->number; j++)
2180 if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j]))
2181 goto skip_ns;
2184 * Exclude extension-element namespaces.
2186 if (extElemNs) {
2187 for (j = 0; j < extElemNs->number; j++)
2188 if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j]))
2189 goto skip_ns;
2192 add_effective_ns:
2194 * OPTIMIZE TODO: This information may not be needed.
2196 if (isLRE && (elem->nsDef != NULL)) {
2197 holdByElem = 0;
2198 tmpns = elem->nsDef;
2199 do {
2200 if (tmpns == ns) {
2201 holdByElem = 1;
2202 break;
2204 tmpns = tmpns->next;
2205 } while (tmpns != NULL);
2206 } else
2207 holdByElem = 0;
2211 * Add the effective namespace declaration.
2213 effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs));
2214 if (effNs == NULL) {
2215 xsltTransformError(NULL, cctxt->style, elem,
2216 "Internal error in xsltLREBuildEffectiveNs(): "
2217 "failed to allocate memory.\n");
2218 cctxt->style->errors++;
2219 return(-1);
2221 if (cctxt->psData->effectiveNs == NULL) {
2222 cctxt->psData->effectiveNs = effNs;
2223 effNs->nextInStore = NULL;
2224 } else {
2225 effNs->nextInStore = cctxt->psData->effectiveNs;
2226 cctxt->psData->effectiveNs = effNs;
2229 effNs->next = NULL;
2230 effNs->prefix = ns->prefix;
2231 effNs->nsName = ns->href;
2232 effNs->holdByElem = holdByElem;
2234 if (lastEffNs == NULL)
2235 item->effectiveNs = effNs;
2236 else
2237 lastEffNs->next = effNs;
2238 lastEffNs = effNs;
2240 skip_ns:
2243 return(0);
2248 * xsltLREInfoCreate:
2250 * @isLRE: indicates if the given @elem is a literal result element
2252 * Creates a new info for a literal result element.
2254 static int
2255 xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
2256 xmlNodePtr elem,
2257 int isLRE)
2259 xsltStyleItemLRElementInfoPtr item;
2261 if ((cctxt == NULL) || (cctxt->inode == NULL))
2262 return(-1);
2264 item = (xsltStyleItemLRElementInfoPtr)
2265 xmlMalloc(sizeof(xsltStyleItemLRElementInfo));
2266 if (item == NULL) {
2267 xsltTransformError(NULL, cctxt->style, NULL,
2268 "Internal error in xsltLREInfoCreate(): "
2269 "memory allocation failed.\n");
2270 cctxt->style->errors++;
2271 return(-1);
2273 memset(item, 0, sizeof(xsltStyleItemLRElementInfo));
2274 item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT;
2276 * Store it in the stylesheet.
2278 item->next = cctxt->style->preComps;
2279 cctxt->style->preComps = (xsltElemPreCompPtr) item;
2281 * @inScopeNs are used for execution of XPath expressions
2282 * in AVTs.
2284 item->inScopeNs = cctxt->inode->inScopeNs;
2286 if (elem)
2287 xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE);
2289 cctxt->inode->litResElemInfo = item;
2290 cctxt->inode->nsChanged = 0;
2291 cctxt->maxLREs++;
2292 return(0);
2296 * xsltCompilerVarInfoPush:
2297 * @cctxt: the compilation context
2299 * Pushes a new var/param info onto the stack.
2301 * Returns the acquired variable info.
2303 static xsltVarInfoPtr
2304 xsltCompilerVarInfoPush(xsltCompilerCtxtPtr cctxt,
2305 xmlNodePtr inst,
2306 const xmlChar *name,
2307 const xmlChar *nsName)
2309 xsltVarInfoPtr ivar;
2311 if ((cctxt->ivar != NULL) && (cctxt->ivar->next != NULL)) {
2312 ivar = cctxt->ivar->next;
2313 } else if ((cctxt->ivar == NULL) && (cctxt->ivars != NULL)) {
2314 ivar = cctxt->ivars;
2315 } else {
2316 ivar = (xsltVarInfoPtr) xmlMalloc(sizeof(xsltVarInfo));
2317 if (ivar == NULL) {
2318 xsltTransformError(NULL, cctxt->style, inst,
2319 "xsltParseInScopeVarPush: xmlMalloc() failed!\n");
2320 cctxt->style->errors++;
2321 return(NULL);
2323 /* memset(retVar, 0, sizeof(xsltInScopeVar)); */
2324 if (cctxt->ivars == NULL) {
2325 cctxt->ivars = ivar;
2326 ivar->prev = NULL;
2327 } else {
2328 cctxt->ivar->next = ivar;
2329 ivar->prev = cctxt->ivar;
2331 cctxt->ivar = ivar;
2332 ivar->next = NULL;
2334 ivar->depth = cctxt->depth;
2335 ivar->name = name;
2336 ivar->nsName = nsName;
2337 return(ivar);
2341 * xsltCompilerVarInfoPop:
2342 * @cctxt: the compilation context
2344 * Pops all var/param infos from the stack, which
2345 * have the current depth.
2347 static void
2348 xsltCompilerVarInfoPop(xsltCompilerCtxtPtr cctxt)
2351 while ((cctxt->ivar != NULL) &&
2352 (cctxt->ivar->depth > cctxt->depth))
2354 cctxt->ivar = cctxt->ivar->prev;
2359 * xsltCompilerNodePush:
2361 * @cctxt: the compilation context
2362 * @node: the node to be pushed (this can also be the doc-node)
2366 * Returns the current node info structure or
2367 * NULL in case of an internal error.
2369 static xsltCompilerNodeInfoPtr
2370 xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
2372 xsltCompilerNodeInfoPtr inode, iprev;
2374 if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) {
2375 inode = cctxt->inode->next;
2376 } else if ((cctxt->inode == NULL) && (cctxt->inodeList != NULL)) {
2377 inode = cctxt->inodeList;
2378 } else {
2380 * Create a new node-info.
2382 inode = (xsltCompilerNodeInfoPtr)
2383 xmlMalloc(sizeof(xsltCompilerNodeInfo));
2384 if (inode == NULL) {
2385 xsltTransformError(NULL, cctxt->style, NULL,
2386 "xsltCompilerNodePush: malloc failed.\n");
2387 return(NULL);
2389 memset(inode, 0, sizeof(xsltCompilerNodeInfo));
2390 if (cctxt->inodeList == NULL)
2391 cctxt->inodeList = inode;
2392 else {
2393 cctxt->inodeLast->next = inode;
2394 inode->prev = cctxt->inodeLast;
2396 cctxt->inodeLast = inode;
2397 cctxt->maxNodeInfos++;
2398 if (cctxt->inode == NULL) {
2399 cctxt->inode = inode;
2401 * Create an initial literal result element info for
2402 * the root of the stylesheet.
2404 xsltLREInfoCreate(cctxt, NULL, 0);
2407 cctxt->depth++;
2408 cctxt->inode = inode;
2410 * REVISIT TODO: Keep the reset always complete.
2411 * NOTE: Be carefull with the @node, since it might be
2412 * a doc-node.
2414 inode->node = node;
2415 inode->depth = cctxt->depth;
2416 inode->templ = NULL;
2417 inode->category = XSLT_ELEMENT_CATEGORY_XSLT;
2418 inode->type = 0;
2419 inode->item = NULL;
2420 inode->curChildType = 0;
2421 inode->extContentHandled = 0;
2422 inode->isRoot = 0;
2424 if (inode->prev != NULL) {
2425 iprev = inode->prev;
2427 * Inherit the following information:
2428 * ---------------------------------
2430 * In-scope namespaces
2432 inode->inScopeNs = iprev->inScopeNs;
2434 * Info for literal result elements
2436 inode->litResElemInfo = iprev->litResElemInfo;
2437 inode->nsChanged = iprev->nsChanged;
2439 * Excluded result namespaces
2441 inode->exclResultNs = iprev->exclResultNs;
2443 * Extension instruction namespaces
2445 inode->extElemNs = iprev->extElemNs;
2447 * Whitespace preservation
2449 inode->preserveWhitespace = iprev->preserveWhitespace;
2451 * Forwards-compatible mode
2453 inode->forwardsCompat = iprev->forwardsCompat;
2454 } else {
2455 inode->inScopeNs = NULL;
2456 inode->exclResultNs = NULL;
2457 inode->extElemNs = NULL;
2458 inode->preserveWhitespace = 0;
2459 inode->forwardsCompat = 0;
2462 return(inode);
2466 * xsltCompilerNodePop:
2468 * @cctxt: the compilation context
2469 * @node: the node to be pushed (this can also be the doc-node)
2471 * Pops the current node info.
2473 static void
2474 xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
2476 if (cctxt->inode == NULL) {
2477 xmlGenericError(xmlGenericErrorContext,
2478 "xsltCompilerNodePop: Top-node mismatch.\n");
2479 return;
2482 * NOTE: Be carefull with the @node, since it might be
2483 * a doc-node.
2485 if (cctxt->inode->node != node) {
2486 xmlGenericError(xmlGenericErrorContext,
2487 "xsltCompilerNodePop: Node mismatch.\n");
2488 goto mismatch;
2490 if (cctxt->inode->depth != cctxt->depth) {
2491 xmlGenericError(xmlGenericErrorContext,
2492 "xsltCompilerNodePop: Depth mismatch.\n");
2493 goto mismatch;
2495 cctxt->depth--;
2497 * Pop information of variables.
2499 if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth))
2500 xsltCompilerVarInfoPop(cctxt);
2502 cctxt->inode = cctxt->inode->prev;
2503 if (cctxt->inode != NULL)
2504 cctxt->inode->curChildType = 0;
2505 return;
2507 mismatch:
2509 const xmlChar *nsName = NULL, *name = NULL;
2510 const xmlChar *infnsName = NULL, *infname = NULL;
2512 if (node) {
2513 if (node->type == XML_ELEMENT_NODE) {
2514 name = node->name;
2515 if (node->ns != NULL)
2516 nsName = node->ns->href;
2517 else
2518 nsName = BAD_CAST "";
2519 } else {
2520 name = BAD_CAST "#document";
2521 nsName = BAD_CAST "";
2523 } else
2524 name = BAD_CAST "Not given";
2526 if (cctxt->inode->node) {
2527 if (node->type == XML_ELEMENT_NODE) {
2528 infname = cctxt->inode->node->name;
2529 if (cctxt->inode->node->ns != NULL)
2530 infnsName = cctxt->inode->node->ns->href;
2531 else
2532 infnsName = BAD_CAST "";
2533 } else {
2534 infname = BAD_CAST "#document";
2535 infnsName = BAD_CAST "";
2537 } else
2538 infname = BAD_CAST "Not given";
2541 xmlGenericError(xmlGenericErrorContext,
2542 "xsltCompilerNodePop: Given : '%s' URI '%s'\n",
2543 name, nsName);
2544 xmlGenericError(xmlGenericErrorContext,
2545 "xsltCompilerNodePop: Expected: '%s' URI '%s'\n",
2546 infname, infnsName);
2551 * xsltCompilerBuildInScopeNsList:
2553 * Create and store the list of in-scope namespaces for the given
2554 * node in the stylesheet. If there are no changes in the in-scope
2555 * namespaces then the last ns-info of the ancestor axis will be returned.
2556 * Compilation-time only.
2558 * Returns the ns-info or NULL if there are no namespaces in scope.
2560 static xsltNsListContainerPtr
2561 xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
2563 xsltNsListContainerPtr nsi = NULL;
2564 xmlNsPtr *list = NULL, ns;
2565 int i, maxns = 5;
2567 * Create a new ns-list for this position in the node-tree.
2568 * xmlGetNsList() will return NULL, if there are no ns-decls in the
2569 * tree. Note that the ns-decl for the XML namespace is not added
2570 * to the resulting list; the XPath module handles the XML namespace
2571 * internally.
2573 while (node != NULL) {
2574 if (node->type == XML_ELEMENT_NODE) {
2575 ns = node->nsDef;
2576 while (ns != NULL) {
2577 if (nsi == NULL) {
2578 nsi = (xsltNsListContainerPtr)
2579 xmlMalloc(sizeof(xsltNsListContainer));
2580 if (nsi == NULL) {
2581 xsltTransformError(NULL, cctxt->style, NULL,
2582 "xsltCompilerBuildInScopeNsList: "
2583 "malloc failed!\n");
2584 goto internal_err;
2586 memset(nsi, 0, sizeof(xsltNsListContainer));
2587 nsi->list =
2588 (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr));
2589 if (nsi->list == NULL) {
2590 xsltTransformError(NULL, cctxt->style, NULL,
2591 "xsltCompilerBuildInScopeNsList: "
2592 "malloc failed!\n");
2593 goto internal_err;
2595 nsi->list[0] = NULL;
2598 * Skip shadowed namespace bindings.
2600 for (i = 0; i < nsi->totalNumber; i++) {
2601 if ((ns->prefix == nsi->list[i]->prefix) ||
2602 (xmlStrEqual(ns->prefix, nsi->list[i]->prefix)))
2603 break;
2605 if (i >= nsi->totalNumber) {
2606 if (nsi->totalNumber +1 >= maxns) {
2607 maxns *= 2;
2608 nsi->list =
2609 (xmlNsPtr *) xmlRealloc(nsi->list,
2610 maxns * sizeof(xmlNsPtr));
2611 if (nsi->list == NULL) {
2612 xsltTransformError(NULL, cctxt->style, NULL,
2613 "xsltCompilerBuildInScopeNsList: "
2614 "realloc failed!\n");
2615 goto internal_err;
2618 nsi->list[nsi->totalNumber++] = ns;
2619 nsi->list[nsi->totalNumber] = NULL;
2622 ns = ns->next;
2625 node = node->parent;
2627 if (nsi == NULL)
2628 return(NULL);
2630 * Move the default namespace to last position.
2632 nsi->xpathNumber = nsi->totalNumber;
2633 for (i = 0; i < nsi->totalNumber; i++) {
2634 if (nsi->list[i]->prefix == NULL) {
2635 ns = nsi->list[i];
2636 nsi->list[i] = nsi->list[nsi->totalNumber-1];
2637 nsi->list[nsi->totalNumber-1] = ns;
2638 nsi->xpathNumber--;
2639 break;
2643 * Store the ns-list in the stylesheet.
2645 if (xsltPointerListAddSize(
2646 (xsltPointerListPtr)cctxt->psData->inScopeNamespaces,
2647 (void *) nsi, 5) == -1)
2649 xmlFree(nsi);
2650 nsi = NULL;
2651 xsltTransformError(NULL, cctxt->style, NULL,
2652 "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n");
2653 goto internal_err;
2656 * Notify of change in status wrt namespaces.
2658 if (cctxt->inode != NULL)
2659 cctxt->inode->nsChanged = 1;
2661 return(nsi);
2663 internal_err:
2664 if (list != NULL)
2665 xmlFree(list);
2666 cctxt->style->errors++;
2667 return(NULL);
2670 static int
2671 xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt,
2672 xsltPointerListPtr list,
2673 xmlNodePtr node,
2674 const xmlChar *value)
2676 xmlChar *cur, *end;
2677 xmlNsPtr ns;
2679 if ((cctxt == NULL) || (value == NULL) || (list == NULL))
2680 return(-1);
2682 list->number = 0;
2684 cur = (xmlChar *) value;
2685 while (*cur != 0) {
2686 while (IS_BLANK(*cur)) cur++;
2687 if (*cur == 0)
2688 break;
2689 end = cur;
2690 while ((*end != 0) && (!IS_BLANK(*end))) end++;
2691 cur = xmlStrndup(cur, end - cur);
2692 if (cur == NULL) {
2693 cur = end;
2694 continue;
2697 * TODO: Export and use xmlSearchNsByPrefixStrict()
2698 * in Libxml2, tree.c, since xmlSearchNs() is in most
2699 * cases not efficient and in some cases not correct.
2701 * XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value.
2703 if ((cur[0] == '#') &&
2704 xmlStrEqual(cur, (const xmlChar *)"#default"))
2705 ns = xmlSearchNs(cctxt->style->doc, node, NULL);
2706 else
2707 ns = xmlSearchNs(cctxt->style->doc, node, cur);
2709 if (ns == NULL) {
2711 * TODO: Better to report the attr-node, otherwise
2712 * the user won't know which attribute was invalid.
2714 xsltTransformError(NULL, cctxt->style, node,
2715 "No namespace binding in scope for prefix '%s'.\n", cur);
2717 * XSLT-1.0: "It is an error if there is no namespace
2718 * bound to the prefix on the element bearing the
2719 * exclude-result-prefixes or xsl:exclude-result-prefixes
2720 * attribute."
2722 cctxt->style->errors++;
2723 } else {
2724 #ifdef WITH_XSLT_DEBUG_PARSING
2725 xsltGenericDebug(xsltGenericDebugContext,
2726 "resolved prefix '%s'\n", cur);
2727 #endif
2729 * Note that we put the namespace name into the dict.
2731 if (xsltPointerListAddSize(list,
2732 (void *) xmlDictLookup(cctxt->style->dict,
2733 ns->href, -1), 5) == -1)
2735 xmlFree(cur);
2736 goto internal_err;
2739 xmlFree(cur);
2741 cur = end;
2743 return(0);
2745 internal_err:
2746 cctxt->style->errors++;
2747 return(-1);
2751 * xsltCompilerUtilsCreateMergedList:
2752 * @dest: the destination list (optional)
2753 * @first: the first list
2754 * @second: the second list (optional)
2756 * Appends the content of @second to @first into @destination.
2757 * If @destination is NULL a new list will be created.
2759 * Returns the merged list of items or NULL if there's nothing to merge.
2761 static xsltPointerListPtr
2762 xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first,
2763 xsltPointerListPtr second)
2765 xsltPointerListPtr ret;
2766 size_t num;
2768 if (first)
2769 num = first->number;
2770 else
2771 num = 0;
2772 if (second)
2773 num += second->number;
2774 if (num == 0)
2775 return(NULL);
2776 ret = xsltPointerListCreate(num);
2777 if (ret == NULL)
2778 return(NULL);
2780 * Copy contents.
2782 if ((first != NULL) && (first->number != 0)) {
2783 memcpy(ret->items, first->items,
2784 first->number * sizeof(void *));
2785 if ((second != NULL) && (second->number != 0))
2786 memcpy(ret->items + first->number, second->items,
2787 second->number * sizeof(void *));
2788 } else if ((second != NULL) && (second->number != 0))
2789 memcpy(ret->items, (void *) second->items,
2790 second->number * sizeof(void *));
2791 ret->number = num;
2792 return(ret);
2796 * xsltParseExclResultPrefixes:
2798 * Create and store the list of in-scope namespaces for the given
2799 * node in the stylesheet. If there are no changes in the in-scope
2800 * namespaces then the last ns-info of the ancestor axis will be returned.
2801 * Compilation-time only.
2803 * Returns the ns-info or NULL if there are no namespaces in scope.
2805 static xsltPointerListPtr
2806 xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
2807 xsltPointerListPtr def,
2808 int instrCategory)
2810 xsltPointerListPtr list = NULL;
2811 xmlChar *value;
2812 xmlAttrPtr attr;
2814 if ((cctxt == NULL) || (node == NULL))
2815 return(NULL);
2817 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
2818 attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL);
2819 else
2820 attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes",
2821 XSLT_NAMESPACE);
2822 if (attr == NULL)
2823 return(def);
2825 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
2827 * Mark the XSLT attr.
2829 attr->psvi = (void *) xsltXSLTAttrMarker;
2832 if ((attr->children != NULL) &&
2833 (attr->children->content != NULL))
2834 value = attr->children->content;
2835 else {
2836 xsltTransformError(NULL, cctxt->style, node,
2837 "Attribute 'exclude-result-prefixes': Invalid value.\n");
2838 cctxt->style->errors++;
2839 return(def);
2842 if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
2843 BAD_CAST value) != 0)
2844 goto exit;
2845 if (cctxt->tmpList->number == 0)
2846 goto exit;
2848 * Merge the list with the inherited list.
2850 list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList);
2851 if (list == NULL)
2852 goto exit;
2854 * Store the list in the stylesheet/compiler context.
2856 if (xsltPointerListAddSize(
2857 cctxt->psData->exclResultNamespaces, list, 5) == -1)
2859 xsltPointerListFree(list);
2860 list = NULL;
2861 goto exit;
2864 * Notify of change in status wrt namespaces.
2866 if (cctxt->inode != NULL)
2867 cctxt->inode->nsChanged = 1;
2869 exit:
2870 if (list != NULL)
2871 return(list);
2872 else
2873 return(def);
2877 * xsltParseExtElemPrefixes:
2879 * Create and store the list of in-scope namespaces for the given
2880 * node in the stylesheet. If there are no changes in the in-scope
2881 * namespaces then the last ns-info of the ancestor axis will be returned.
2882 * Compilation-time only.
2884 * Returns the ns-info or NULL if there are no namespaces in scope.
2886 static xsltPointerListPtr
2887 xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
2888 xsltPointerListPtr def,
2889 int instrCategory)
2891 xsltPointerListPtr list = NULL;
2892 xmlAttrPtr attr;
2893 xmlChar *value;
2894 int i;
2896 if ((cctxt == NULL) || (node == NULL))
2897 return(NULL);
2899 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
2900 attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL);
2901 else
2902 attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes",
2903 XSLT_NAMESPACE);
2904 if (attr == NULL)
2905 return(def);
2907 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
2909 * Mark the XSLT attr.
2911 attr->psvi = (void *) xsltXSLTAttrMarker;
2914 if ((attr->children != NULL) &&
2915 (attr->children->content != NULL))
2916 value = attr->children->content;
2917 else {
2918 xsltTransformError(NULL, cctxt->style, node,
2919 "Attribute 'extension-element-prefixes': Invalid value.\n");
2920 cctxt->style->errors++;
2921 return(def);
2925 if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
2926 BAD_CAST value) != 0)
2927 goto exit;
2929 if (cctxt->tmpList->number == 0)
2930 goto exit;
2932 * REVISIT: Register the extension namespaces.
2934 for (i = 0; i < cctxt->tmpList->number; i++)
2935 xsltRegisterExtPrefix(cctxt->style, NULL,
2936 BAD_CAST cctxt->tmpList->items[i]);
2938 * Merge the list with the inherited list.
2940 list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList);
2941 if (list == NULL)
2942 goto exit;
2944 * Store the list in the stylesheet.
2946 if (xsltPointerListAddSize(
2947 cctxt->psData->extElemNamespaces, list, 5) == -1)
2949 xsltPointerListFree(list);
2950 list = NULL;
2951 goto exit;
2954 * Notify of change in status wrt namespaces.
2956 if (cctxt->inode != NULL)
2957 cctxt->inode->nsChanged = 1;
2959 exit:
2960 if (list != NULL)
2961 return(list);
2962 else
2963 return(def);
2967 * xsltParseAttrXSLTVersion:
2969 * @cctxt: the compilation context
2970 * @node: the element-node
2971 * @isXsltElem: whether this is an XSLT element
2973 * Parses the attribute xsl:version.
2975 * Returns 1 if there was such an attribute, 0 if not and
2976 * -1 if an internal or API error occured.
2978 static int
2979 xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
2980 int instrCategory)
2982 xmlChar *value;
2983 xmlAttrPtr attr;
2985 if ((cctxt == NULL) || (node == NULL))
2986 return(-1);
2988 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
2989 attr = xmlHasNsProp(node, BAD_CAST "version", NULL);
2990 else
2991 attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE);
2993 if (attr == NULL)
2994 return(0);
2996 attr->psvi = (void *) xsltXSLTAttrMarker;
2998 if ((attr->children != NULL) &&
2999 (attr->children->content != NULL))
3000 value = attr->children->content;
3001 else {
3002 xsltTransformError(NULL, cctxt->style, node,
3003 "Attribute 'version': Invalid value.\n");
3004 cctxt->style->errors++;
3005 return(1);
3008 if (! xmlStrEqual(value, (const xmlChar *)"1.0")) {
3009 cctxt->inode->forwardsCompat = 1;
3011 * TODO: To what extent do we support the
3012 * forwards-compatible mode?
3015 * Report this only once per compilation episode.
3017 if (! cctxt->hasForwardsCompat) {
3018 cctxt->hasForwardsCompat = 1;
3019 cctxt->errSeverity = XSLT_ERROR_SEVERITY_WARNING;
3020 xsltTransformError(NULL, cctxt->style, node,
3021 "Warning: the attribute xsl:version specifies a value "
3022 "different from '1.0'. Switching to forwards-compatible "
3023 "mode. Only features of XSLT 1.0 are supported by this "
3024 "processor.\n");
3025 cctxt->style->warnings++;
3026 cctxt->errSeverity = XSLT_ERROR_SEVERITY_ERROR;
3028 } else {
3029 cctxt->inode->forwardsCompat = 0;
3032 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
3034 * Set a marker on XSLT attributes.
3036 attr->psvi = (void *) xsltXSLTAttrMarker;
3038 return(1);
3041 static int
3042 xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
3044 xmlNodePtr deleteNode, cur, txt, textNode = NULL;
3045 xmlDocPtr doc;
3046 xsltStylesheetPtr style;
3047 int internalize = 0, findSpaceAttr;
3048 int xsltStylesheetElemDepth;
3049 xmlAttrPtr attr;
3050 xmlChar *value;
3051 const xmlChar *name, *nsNameXSLT = NULL;
3052 int strictWhitespace, inXSLText = 0;
3053 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
3054 xsltNsMapPtr nsMapItem;
3055 #endif
3057 if ((cctxt == NULL) || (cctxt->style == NULL) ||
3058 (node == NULL) || (node->type != XML_ELEMENT_NODE))
3059 return(-1);
3061 doc = node->doc;
3062 if (doc == NULL)
3063 goto internal_err;
3065 style = cctxt->style;
3066 if ((style->dict != NULL) && (doc->dict == style->dict))
3067 internalize = 1;
3068 else
3069 style->internalized = 0;
3072 * Init value of xml:space. Since this might be an embedded
3073 * stylesheet, this is needed to be performed on the element
3074 * where the stylesheet is rooted at, taking xml:space of
3075 * ancestors into account.
3077 if (! cctxt->simplified)
3078 xsltStylesheetElemDepth = cctxt->depth +1;
3079 else
3080 xsltStylesheetElemDepth = 0;
3082 if (xmlNodeGetSpacePreserve(node) != 1)
3083 cctxt->inode->preserveWhitespace = 0;
3084 else
3085 cctxt->inode->preserveWhitespace = 1;
3088 * Eval if we should keep the old incorrect behaviour.
3090 strictWhitespace = (cctxt->strict != 0) ? 1 : 0;
3092 nsNameXSLT = xsltConstNamespaceNameXSLT;
3094 deleteNode = NULL;
3095 cur = node;
3096 while (cur != NULL) {
3097 if (deleteNode != NULL) {
3099 #ifdef WITH_XSLT_DEBUG_BLANKS
3100 xsltGenericDebug(xsltGenericDebugContext,
3101 "xsltParsePreprocessStylesheetTree: removing node\n");
3102 #endif
3103 xmlUnlinkNode(deleteNode);
3104 xmlFreeNode(deleteNode);
3105 deleteNode = NULL;
3107 if (cur->type == XML_ELEMENT_NODE) {
3110 * Clear the PSVI field.
3112 cur->psvi = NULL;
3114 xsltCompilerNodePush(cctxt, cur);
3116 inXSLText = 0;
3117 textNode = NULL;
3118 findSpaceAttr = 1;
3119 cctxt->inode->stripWhitespace = 0;
3121 * TODO: I'd love to use a string pointer comparison here :-/
3123 if (IS_XSLT_ELEM(cur)) {
3124 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
3125 if (cur->ns->href != nsNameXSLT) {
3126 nsMapItem = xsltNewNamespaceMapItem(cctxt,
3127 doc, cur->ns, cur);
3128 if (nsMapItem == NULL)
3129 goto internal_err;
3130 cur->ns->href = nsNameXSLT;
3132 #endif
3134 if (cur->name == NULL)
3135 goto process_attributes;
3137 * Mark the XSLT element for later recognition.
3138 * TODO: Using the marker is still too dangerous, since if
3139 * the parsing mechanism leaves out an XSLT element, then
3140 * this might hit the transformation-mechanism, which
3141 * will break if it doesn't expect such a marker.
3143 /* cur->psvi = (void *) xsltXSLTElemMarker; */
3146 * XSLT 2.0: "Any whitespace text node whose parent is
3147 * one of the following elements is removed from the "
3148 * tree, regardless of any xml:space attributes:..."
3149 * xsl:apply-imports,
3150 * xsl:apply-templates,
3151 * xsl:attribute-set,
3152 * xsl:call-template,
3153 * xsl:choose,
3154 * xsl:stylesheet, xsl:transform.
3155 * XSLT 2.0: xsl:analyze-string,
3156 * xsl:character-map,
3157 * xsl:next-match
3159 * TODO: I'd love to use a string pointer comparison here :-/
3161 name = cur->name;
3162 switch (*name) {
3163 case 't':
3164 if ((name[0] == 't') && (name[1] == 'e') &&
3165 (name[2] == 'x') && (name[3] == 't') &&
3166 (name[4] == 0))
3169 * Process the xsl:text element.
3170 * ----------------------------
3171 * Mark it for later recognition.
3173 cur->psvi = (void *) xsltXSLTTextMarker;
3175 * For stylesheets, the set of
3176 * whitespace-preserving element names
3177 * consists of just xsl:text.
3179 findSpaceAttr = 0;
3180 cctxt->inode->preserveWhitespace = 1;
3181 inXSLText = 1;
3183 break;
3184 case 'c':
3185 if (xmlStrEqual(name, BAD_CAST "choose") ||
3186 xmlStrEqual(name, BAD_CAST "call-template"))
3187 cctxt->inode->stripWhitespace = 1;
3188 break;
3189 case 'a':
3190 if (xmlStrEqual(name, BAD_CAST "apply-templates") ||
3191 xmlStrEqual(name, BAD_CAST "apply-imports") ||
3192 xmlStrEqual(name, BAD_CAST "attribute-set"))
3194 cctxt->inode->stripWhitespace = 1;
3195 break;
3196 default:
3197 if (xsltStylesheetElemDepth == cctxt->depth) {
3199 * This is a xsl:stylesheet/xsl:transform.
3201 cctxt->inode->stripWhitespace = 1;
3202 break;
3205 if ((cur->prev != NULL) &&
3206 (cur->prev->type == XML_TEXT_NODE))
3209 * XSLT 2.0 : "Any whitespace text node whose
3210 * following-sibling node is an xsl:param or
3211 * xsl:sort element is removed from the tree,
3212 * regardless of any xml:space attributes."
3214 if (((*name == 'p') || (*name == 's')) &&
3215 (xmlStrEqual(name, BAD_CAST "param") ||
3216 xmlStrEqual(name, BAD_CAST "sort")))
3218 do {
3219 if (IS_BLANK_NODE(cur->prev)) {
3220 txt = cur->prev;
3221 xmlUnlinkNode(txt);
3222 xmlFreeNode(txt);
3223 } else {
3225 * This will result in a content
3226 * error, when hitting the parsing
3227 * functions.
3229 break;
3231 } while (cur->prev);
3234 break;
3238 process_attributes:
3240 * Process attributes.
3241 * ------------------
3243 if (cur->properties != NULL) {
3244 if (cur->children == NULL)
3245 findSpaceAttr = 0;
3246 attr = cur->properties;
3247 do {
3248 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
3249 if ((attr->ns) && (attr->ns->href != nsNameXSLT) &&
3250 xmlStrEqual(attr->ns->href, nsNameXSLT))
3252 nsMapItem = xsltNewNamespaceMapItem(cctxt,
3253 doc, attr->ns, cur);
3254 if (nsMapItem == NULL)
3255 goto internal_err;
3256 attr->ns->href = nsNameXSLT;
3258 #endif
3259 if (internalize) {
3261 * Internalize the attribute's value; the goal is to
3262 * speed up operations and minimize used space by
3263 * compiled stylesheets.
3265 txt = attr->children;
3267 * NOTE that this assumes only one
3268 * text-node in the attribute's content.
3270 if ((txt != NULL) && (txt->content != NULL) &&
3271 (!xmlDictOwns(style->dict, txt->content)))
3273 value = (xmlChar *) xmlDictLookup(style->dict,
3274 txt->content, -1);
3275 xmlNodeSetContent(txt, NULL);
3276 txt->content = value;
3280 * Process xml:space attributes.
3281 * ----------------------------
3283 if ((findSpaceAttr != 0) &&
3284 (attr->ns != NULL) &&
3285 (attr->name != NULL) &&
3286 (attr->name[0] == 's') &&
3287 (attr->ns->prefix != NULL) &&
3288 (attr->ns->prefix[0] == 'x') &&
3289 (attr->ns->prefix[1] == 'm') &&
3290 (attr->ns->prefix[2] == 'l') &&
3291 (attr->ns->prefix[3] == 0))
3293 value = xmlGetNsProp(cur, BAD_CAST "space",
3294 XML_XML_NAMESPACE);
3295 if (value != NULL) {
3296 if (xmlStrEqual(value, BAD_CAST "preserve")) {
3297 cctxt->inode->preserveWhitespace = 1;
3298 } else if (xmlStrEqual(value, BAD_CAST "default")) {
3299 cctxt->inode->preserveWhitespace = 0;
3300 } else {
3301 /* Invalid value for xml:space. */
3302 xsltTransformError(NULL, style, cur,
3303 "Attribute xml:space: Invalid value.\n");
3304 cctxt->style->warnings++;
3306 findSpaceAttr = 0;
3307 xmlFree(value);
3311 attr = attr->next;
3312 } while (attr != NULL);
3315 * We'll descend into the children of element nodes only.
3317 if (cur->children != NULL) {
3318 cur = cur->children;
3319 continue;
3321 } else if ((cur->type == XML_TEXT_NODE) ||
3322 (cur->type == XML_CDATA_SECTION_NODE))
3325 * Merge adjacent text/CDATA-section-nodes
3326 * ---------------------------------------
3327 * In order to avoid breaking of existing stylesheets,
3328 * if the old behaviour is wanted (strictWhitespace == 0),
3329 * then we *won't* merge adjacent text-nodes
3330 * (except in xsl:text); this will ensure that whitespace-only
3331 * text nodes are (incorrectly) not stripped in some cases.
3333 * Example: : <foo> <!-- bar -->zoo</foo>
3334 * Corrent (strict) result: <foo> zoo</foo>
3335 * Incorrect (old) result : <foo>zoo</foo>
3337 * NOTE that we *will* merge adjacent text-nodes if
3338 * they are in xsl:text.
3339 * Example, the following:
3340 * <xsl:text> <!-- bar -->zoo<xsl:text>
3341 * will result in both cases in:
3342 * <xsl:text> zoo<xsl:text>
3344 cur->type = XML_TEXT_NODE;
3345 if ((strictWhitespace != 0) || (inXSLText != 0)) {
3347 * New behaviour; merge nodes.
3349 if (textNode == NULL)
3350 textNode = cur;
3351 else {
3352 if (cur->content != NULL)
3353 xmlNodeAddContent(textNode, cur->content);
3354 deleteNode = cur;
3356 if ((cur->next == NULL) ||
3357 (cur->next->type == XML_ELEMENT_NODE))
3358 goto end_of_text;
3359 else
3360 goto next_sibling;
3361 } else {
3363 * Old behaviour.
3365 if (textNode == NULL)
3366 textNode = cur;
3367 goto end_of_text;
3369 } else if ((cur->type == XML_COMMENT_NODE) ||
3370 (cur->type == XML_PI_NODE))
3373 * Remove processing instructions and comments.
3375 deleteNode = cur;
3376 if ((cur->next == NULL) ||
3377 (cur->next->type == XML_ELEMENT_NODE))
3378 goto end_of_text;
3379 else
3380 goto next_sibling;
3381 } else {
3382 textNode = NULL;
3384 * Invalid node-type for this data-model.
3386 xsltTransformError(NULL, style, cur,
3387 "Invalid type of node for the XSLT data model.\n");
3388 cctxt->style->errors++;
3389 goto next_sibling;
3392 end_of_text:
3393 if (textNode) {
3394 value = textNode->content;
3396 * At this point all adjacent text/CDATA-section nodes
3397 * have been merged.
3399 * Strip whitespace-only text-nodes.
3400 * (cctxt->inode->stripWhitespace)
3402 if ((value == NULL) || (*value == 0) ||
3403 (((cctxt->inode->stripWhitespace) ||
3404 (! cctxt->inode->preserveWhitespace)) &&
3405 IS_BLANK(*value) &&
3406 xsltIsBlank(value)))
3408 if (textNode != cur) {
3409 xmlUnlinkNode(textNode);
3410 xmlFreeNode(textNode);
3411 } else
3412 deleteNode = textNode;
3413 textNode = NULL;
3414 goto next_sibling;
3417 * Convert CDATA-section nodes to text-nodes.
3418 * TODO: Can this produce problems?
3420 if (textNode->type != XML_TEXT_NODE) {
3421 textNode->type = XML_TEXT_NODE;
3422 textNode->name = xmlStringText;
3424 if (internalize &&
3425 (textNode->content != NULL) &&
3426 (!xmlDictOwns(style->dict, textNode->content)))
3429 * Internalize the string.
3431 value = (xmlChar *) xmlDictLookup(style->dict,
3432 textNode->content, -1);
3433 xmlNodeSetContent(textNode, NULL);
3434 textNode->content = value;
3436 textNode = NULL;
3438 * Note that "disable-output-escaping" of the xsl:text
3439 * element will be applied at a later level, when
3440 * XSLT elements are processed.
3444 next_sibling:
3445 if (cur->type == XML_ELEMENT_NODE) {
3446 xsltCompilerNodePop(cctxt, cur);
3448 if (cur == node)
3449 break;
3450 if (cur->next != NULL) {
3451 cur = cur->next;
3452 } else {
3453 cur = cur->parent;
3454 inXSLText = 0;
3455 goto next_sibling;
3458 if (deleteNode != NULL) {
3459 #ifdef WITH_XSLT_DEBUG_PARSING
3460 xsltGenericDebug(xsltGenericDebugContext,
3461 "xsltParsePreprocessStylesheetTree: removing node\n");
3462 #endif
3463 xmlUnlinkNode(deleteNode);
3464 xmlFreeNode(deleteNode);
3466 return(0);
3468 internal_err:
3469 return(-1);
3472 #endif /* XSLT_REFACTORED */
3474 #ifdef XSLT_REFACTORED
3475 #else
3476 static void
3477 xsltPreprocessStylesheet(xsltStylesheetPtr style, xmlNodePtr cur)
3479 xmlNodePtr deleteNode, styleelem;
3480 int internalize = 0;
3482 if ((style == NULL) || (cur == NULL))
3483 return;
3485 if ((cur->doc != NULL) && (style->dict != NULL) &&
3486 (cur->doc->dict == style->dict))
3487 internalize = 1;
3488 else
3489 style->internalized = 0;
3491 if ((cur != NULL) && (IS_XSLT_ELEM(cur)) &&
3492 (IS_XSLT_NAME(cur, "stylesheet"))) {
3493 styleelem = cur;
3494 } else {
3495 styleelem = NULL;
3499 * This content comes from the stylesheet
3500 * For stylesheets, the set of whitespace-preserving
3501 * element names consists of just xsl:text.
3503 deleteNode = NULL;
3504 while (cur != NULL) {
3505 if (deleteNode != NULL) {
3506 #ifdef WITH_XSLT_DEBUG_BLANKS
3507 xsltGenericDebug(xsltGenericDebugContext,
3508 "xsltPreprocessStylesheet: removing ignorable blank node\n");
3509 #endif
3510 xmlUnlinkNode(deleteNode);
3511 xmlFreeNode(deleteNode);
3512 deleteNode = NULL;
3514 if (cur->type == XML_ELEMENT_NODE) {
3515 int exclPrefixes;
3517 * Internalize attributes values.
3519 if ((internalize) && (cur->properties != NULL)) {
3520 xmlAttrPtr attr = cur->properties;
3521 xmlNodePtr txt;
3523 while (attr != NULL) {
3524 txt = attr->children;
3525 if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
3526 (txt->content != NULL) &&
3527 (!xmlDictOwns(style->dict, txt->content)))
3529 xmlChar *tmp;
3532 * internalize the text string, goal is to speed
3533 * up operations and minimize used space by compiled
3534 * stylesheets.
3536 tmp = (xmlChar *) xmlDictLookup(style->dict,
3537 txt->content, -1);
3538 if (tmp != txt->content) {
3539 xmlNodeSetContent(txt, NULL);
3540 txt->content = tmp;
3543 attr = attr->next;
3546 if (IS_XSLT_ELEM(cur)) {
3547 exclPrefixes = 0;
3548 if (IS_XSLT_NAME(cur, "text")) {
3549 for (;exclPrefixes > 0;exclPrefixes--)
3550 exclPrefixPop(style);
3551 goto skip_children;
3553 } else {
3554 exclPrefixes = xsltParseStylesheetExcludePrefix(style, cur, 0);
3557 if ((cur->nsDef != NULL) && (style->exclPrefixNr > 0)) {
3558 xmlNsPtr ns = cur->nsDef, prev = NULL, next;
3559 xmlNodePtr root = NULL;
3560 int i, moved;
3562 root = xmlDocGetRootElement(cur->doc);
3563 if ((root != NULL) && (root != cur)) {
3564 while (ns != NULL) {
3565 moved = 0;
3566 next = ns->next;
3567 for (i = 0;i < style->exclPrefixNr;i++) {
3568 if ((ns->prefix != NULL) &&
3569 (xmlStrEqual(ns->href,
3570 style->exclPrefixTab[i]))) {
3572 * Move the namespace definition on the root
3573 * element to avoid duplicating it without
3574 * loosing it.
3576 if (prev == NULL) {
3577 cur->nsDef = ns->next;
3578 } else {
3579 prev->next = ns->next;
3581 ns->next = root->nsDef;
3582 root->nsDef = ns;
3583 moved = 1;
3584 break;
3587 if (moved == 0)
3588 prev = ns;
3589 ns = next;
3594 * If we have prefixes locally, recurse and pop them up when
3595 * going back
3597 if (exclPrefixes > 0) {
3598 xsltPreprocessStylesheet(style, cur->children);
3599 for (;exclPrefixes > 0;exclPrefixes--)
3600 exclPrefixPop(style);
3601 goto skip_children;
3603 } else if (cur->type == XML_TEXT_NODE) {
3604 if (IS_BLANK_NODE(cur)) {
3605 if (xmlNodeGetSpacePreserve(cur->parent) != 1) {
3606 deleteNode = cur;
3608 } else if ((cur->content != NULL) && (internalize) &&
3609 (!xmlDictOwns(style->dict, cur->content))) {
3610 xmlChar *tmp;
3613 * internalize the text string, goal is to speed
3614 * up operations and minimize used space by compiled
3615 * stylesheets.
3617 tmp = (xmlChar *) xmlDictLookup(style->dict, cur->content, -1);
3618 xmlNodeSetContent(cur, NULL);
3619 cur->content = tmp;
3621 } else if ((cur->type != XML_ELEMENT_NODE) &&
3622 (cur->type != XML_CDATA_SECTION_NODE)) {
3623 deleteNode = cur;
3624 goto skip_children;
3628 * Skip to next node. In case of a namespaced element children of
3629 * the stylesheet and not in the XSLT namespace and not an extension
3630 * element, ignore its content.
3632 if ((cur->type == XML_ELEMENT_NODE) && (cur->ns != NULL) &&
3633 (styleelem != NULL) && (cur->parent == styleelem) &&
3634 (!xmlStrEqual(cur->ns->href, XSLT_NAMESPACE)) &&
3635 (!xsltCheckExtURI(style, cur->ns->href))) {
3636 goto skip_children;
3637 } else if (cur->children != NULL) {
3638 cur = cur->children;
3639 continue;
3642 skip_children:
3643 if (cur->next != NULL) {
3644 cur = cur->next;
3645 continue;
3647 do {
3649 cur = cur->parent;
3650 if (cur == NULL)
3651 break;
3652 if (cur == (xmlNodePtr) style->doc) {
3653 cur = NULL;
3654 break;
3656 if (cur->next != NULL) {
3657 cur = cur->next;
3658 break;
3660 } while (cur != NULL);
3662 if (deleteNode != NULL) {
3663 #ifdef WITH_XSLT_DEBUG_PARSING
3664 xsltGenericDebug(xsltGenericDebugContext,
3665 "xsltPreprocessStylesheet: removing ignorable blank node\n");
3666 #endif
3667 xmlUnlinkNode(deleteNode);
3668 xmlFreeNode(deleteNode);
3671 #endif /* end of else XSLT_REFACTORED */
3674 * xsltGatherNamespaces:
3675 * @style: the XSLT stylesheet
3677 * Browse the stylesheet and build the namspace hash table which
3678 * will be used for XPath interpretation. If needed do a bit of normalization
3681 static void
3682 xsltGatherNamespaces(xsltStylesheetPtr style) {
3683 xmlNodePtr cur;
3684 const xmlChar *URI;
3686 if (style == NULL)
3687 return;
3689 * TODO: basically if the stylesheet uses the same prefix for different
3690 * patterns, well they may be in problem, hopefully they will get
3691 * a warning first.
3694 * TODO: Eliminate the use of the hash for XPath expressions.
3695 * An expression should be evaluated in the context of the in-scope
3696 * namespaces; eliminate the restriction of an XML document to contain
3697 * no duplicate prefixes for different namespace names.
3700 cur = xmlDocGetRootElement(style->doc);
3701 while (cur != NULL) {
3702 if (cur->type == XML_ELEMENT_NODE) {
3703 xmlNsPtr ns = cur->nsDef;
3704 while (ns != NULL) {
3705 if (ns->prefix != NULL) {
3706 if (style->nsHash == NULL) {
3707 style->nsHash = xmlHashCreate(10);
3708 if (style->nsHash == NULL) {
3709 xsltTransformError(NULL, style, cur,
3710 "xsltGatherNamespaces: failed to create hash table\n");
3711 style->errors++;
3712 return;
3715 URI = xmlHashLookup(style->nsHash, ns->prefix);
3716 if ((URI != NULL) && (!xmlStrEqual(URI, ns->href))) {
3717 xsltTransformError(NULL, style, cur,
3718 "Namespaces prefix %s used for multiple namespaces\n",ns->prefix);
3719 style->warnings++;
3720 } else if (URI == NULL) {
3721 xmlHashUpdateEntry(style->nsHash, ns->prefix,
3722 (void *) ns->href, NULL);
3724 #ifdef WITH_XSLT_DEBUG_PARSING
3725 xsltGenericDebug(xsltGenericDebugContext,
3726 "Added namespace: %s mapped to %s\n", ns->prefix, ns->href);
3727 #endif
3730 ns = ns->next;
3735 * Skip to next node
3737 if (cur->children != NULL) {
3738 if (cur->children->type != XML_ENTITY_DECL) {
3739 cur = cur->children;
3740 continue;
3743 if (cur->next != NULL) {
3744 cur = cur->next;
3745 continue;
3748 do {
3749 cur = cur->parent;
3750 if (cur == NULL)
3751 break;
3752 if (cur == (xmlNodePtr) style->doc) {
3753 cur = NULL;
3754 break;
3756 if (cur->next != NULL) {
3757 cur = cur->next;
3758 break;
3760 } while (cur != NULL);
3764 #ifdef XSLT_REFACTORED
3766 static xsltStyleType
3767 xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt,
3768 xmlNodePtr node)
3770 if ((node == NULL) || (node->type != XML_ELEMENT_NODE) ||
3771 (node->name == NULL))
3772 return(0);
3774 if (node->name[0] == 'a') {
3775 if (IS_XSLT_NAME(node, "apply-templates"))
3776 return(XSLT_FUNC_APPLYTEMPLATES);
3777 else if (IS_XSLT_NAME(node, "attribute"))
3778 return(XSLT_FUNC_ATTRIBUTE);
3779 else if (IS_XSLT_NAME(node, "apply-imports"))
3780 return(XSLT_FUNC_APPLYIMPORTS);
3781 else if (IS_XSLT_NAME(node, "attribute-set"))
3782 return(0);
3784 } else if (node->name[0] == 'c') {
3785 if (IS_XSLT_NAME(node, "choose"))
3786 return(XSLT_FUNC_CHOOSE);
3787 else if (IS_XSLT_NAME(node, "copy"))
3788 return(XSLT_FUNC_COPY);
3789 else if (IS_XSLT_NAME(node, "copy-of"))
3790 return(XSLT_FUNC_COPYOF);
3791 else if (IS_XSLT_NAME(node, "call-template"))
3792 return(XSLT_FUNC_CALLTEMPLATE);
3793 else if (IS_XSLT_NAME(node, "comment"))
3794 return(XSLT_FUNC_COMMENT);
3796 } else if (node->name[0] == 'd') {
3797 if (IS_XSLT_NAME(node, "document"))
3798 return(XSLT_FUNC_DOCUMENT);
3799 else if (IS_XSLT_NAME(node, "decimal-format"))
3800 return(0);
3802 } else if (node->name[0] == 'e') {
3803 if (IS_XSLT_NAME(node, "element"))
3804 return(XSLT_FUNC_ELEMENT);
3806 } else if (node->name[0] == 'f') {
3807 if (IS_XSLT_NAME(node, "for-each"))
3808 return(XSLT_FUNC_FOREACH);
3809 else if (IS_XSLT_NAME(node, "fallback"))
3810 return(XSLT_FUNC_FALLBACK);
3812 } else if (*(node->name) == 'i') {
3813 if (IS_XSLT_NAME(node, "if"))
3814 return(XSLT_FUNC_IF);
3815 else if (IS_XSLT_NAME(node, "include"))
3816 return(0);
3817 else if (IS_XSLT_NAME(node, "import"))
3818 return(0);
3820 } else if (*(node->name) == 'k') {
3821 if (IS_XSLT_NAME(node, "key"))
3822 return(0);
3824 } else if (*(node->name) == 'm') {
3825 if (IS_XSLT_NAME(node, "message"))
3826 return(XSLT_FUNC_MESSAGE);
3828 } else if (*(node->name) == 'n') {
3829 if (IS_XSLT_NAME(node, "number"))
3830 return(XSLT_FUNC_NUMBER);
3831 else if (IS_XSLT_NAME(node, "namespace-alias"))
3832 return(0);
3834 } else if (*(node->name) == 'o') {
3835 if (IS_XSLT_NAME(node, "otherwise"))
3836 return(XSLT_FUNC_OTHERWISE);
3837 else if (IS_XSLT_NAME(node, "output"))
3838 return(0);
3840 } else if (*(node->name) == 'p') {
3841 if (IS_XSLT_NAME(node, "param"))
3842 return(XSLT_FUNC_PARAM);
3843 else if (IS_XSLT_NAME(node, "processing-instruction"))
3844 return(XSLT_FUNC_PI);
3845 else if (IS_XSLT_NAME(node, "preserve-space"))
3846 return(0);
3848 } else if (*(node->name) == 's') {
3849 if (IS_XSLT_NAME(node, "sort"))
3850 return(XSLT_FUNC_SORT);
3851 else if (IS_XSLT_NAME(node, "strip-space"))
3852 return(0);
3853 else if (IS_XSLT_NAME(node, "stylesheet"))
3854 return(0);
3856 } else if (node->name[0] == 't') {
3857 if (IS_XSLT_NAME(node, "text"))
3858 return(XSLT_FUNC_TEXT);
3859 else if (IS_XSLT_NAME(node, "template"))
3860 return(0);
3861 else if (IS_XSLT_NAME(node, "transform"))
3862 return(0);
3864 } else if (*(node->name) == 'v') {
3865 if (IS_XSLT_NAME(node, "value-of"))
3866 return(XSLT_FUNC_VALUEOF);
3867 else if (IS_XSLT_NAME(node, "variable"))
3868 return(XSLT_FUNC_VARIABLE);
3870 } else if (*(node->name) == 'w') {
3871 if (IS_XSLT_NAME(node, "when"))
3872 return(XSLT_FUNC_WHEN);
3873 if (IS_XSLT_NAME(node, "with-param"))
3874 return(XSLT_FUNC_WITHPARAM);
3876 return(0);
3880 * xsltParseAnyXSLTElem:
3882 * @cctxt: the compilation context
3883 * @elem: the element node of the XSLT instruction
3885 * Parses, validates the content models and compiles XSLT instructions.
3887 * Returns 0 if everything's fine;
3888 * -1 on API or internal errors.
3891 xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem)
3893 if ((cctxt == NULL) || (elem == NULL) ||
3894 (elem->type != XML_ELEMENT_NODE))
3895 return(-1);
3897 elem->psvi = NULL;
3899 if (! (IS_XSLT_ELEM_FAST(elem)))
3900 return(-1);
3902 * Detection of handled content of extension instructions.
3904 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
3905 cctxt->inode->extContentHandled = 1;
3908 xsltCompilerNodePush(cctxt, elem);
3910 * URGENT TODO: Find a way to speed up this annoying redundant
3911 * textual node-name and namespace comparison.
3913 if (cctxt->inode->prev->curChildType != 0)
3914 cctxt->inode->type = cctxt->inode->prev->curChildType;
3915 else
3916 cctxt->inode->type = xsltGetXSLTElementTypeByNode(cctxt, elem);
3918 * Update the in-scope namespaces if needed.
3920 if (elem->nsDef != NULL)
3921 cctxt->inode->inScopeNs =
3922 xsltCompilerBuildInScopeNsList(cctxt, elem);
3924 * xsltStylePreCompute():
3925 * This will compile the information found on the current
3926 * element's attributes. NOTE that this won't process the
3927 * children of the instruction.
3929 xsltStylePreCompute(cctxt->style, elem);
3931 * TODO: How to react on errors in xsltStylePreCompute() ?
3935 * Validate the content model of the XSLT-element.
3937 switch (cctxt->inode->type) {
3938 case XSLT_FUNC_APPLYIMPORTS:
3939 /* EMPTY */
3940 goto empty_content;
3941 case XSLT_FUNC_APPLYTEMPLATES:
3942 /* <!-- Content: (xsl:sort | xsl:with-param)* --> */
3943 goto apply_templates;
3944 case XSLT_FUNC_ATTRIBUTE:
3945 /* <!-- Content: template --> */
3946 goto sequence_constructor;
3947 case XSLT_FUNC_CALLTEMPLATE:
3948 /* <!-- Content: xsl:with-param* --> */
3949 goto call_template;
3950 case XSLT_FUNC_CHOOSE:
3951 /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */
3952 goto choose;
3953 case XSLT_FUNC_COMMENT:
3954 /* <!-- Content: template --> */
3955 goto sequence_constructor;
3956 case XSLT_FUNC_COPY:
3957 /* <!-- Content: template --> */
3958 goto sequence_constructor;
3959 case XSLT_FUNC_COPYOF:
3960 /* EMPTY */
3961 goto empty_content;
3962 case XSLT_FUNC_DOCUMENT: /* Extra one */
3963 /* ?? template ?? */
3964 goto sequence_constructor;
3965 case XSLT_FUNC_ELEMENT:
3966 /* <!-- Content: template --> */
3967 goto sequence_constructor;
3968 case XSLT_FUNC_FALLBACK:
3969 /* <!-- Content: template --> */
3970 goto sequence_constructor;
3971 case XSLT_FUNC_FOREACH:
3972 /* <!-- Content: (xsl:sort*, template) --> */
3973 goto for_each;
3974 case XSLT_FUNC_IF:
3975 /* <!-- Content: template --> */
3976 goto sequence_constructor;
3977 case XSLT_FUNC_OTHERWISE:
3978 /* <!-- Content: template --> */
3979 goto sequence_constructor;
3980 case XSLT_FUNC_MESSAGE:
3981 /* <!-- Content: template --> */
3982 goto sequence_constructor;
3983 case XSLT_FUNC_NUMBER:
3984 /* EMPTY */
3985 goto empty_content;
3986 case XSLT_FUNC_PARAM:
3988 * Check for redefinition.
3990 if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
3991 xsltVarInfoPtr ivar = cctxt->ivar;
3993 do {
3994 if ((ivar->name ==
3995 ((xsltStyleItemParamPtr) elem->psvi)->name) &&
3996 (ivar->nsName ==
3997 ((xsltStyleItemParamPtr) elem->psvi)->ns))
3999 elem->psvi = NULL;
4000 xsltTransformError(NULL, cctxt->style, elem,
4001 "Redefinition of variable or parameter '%s'.\n",
4002 ivar->name);
4003 cctxt->style->errors++;
4004 goto error;
4006 ivar = ivar->prev;
4007 } while (ivar != NULL);
4009 /* <!-- Content: template --> */
4010 goto sequence_constructor;
4011 case XSLT_FUNC_PI:
4012 /* <!-- Content: template --> */
4013 goto sequence_constructor;
4014 case XSLT_FUNC_SORT:
4015 /* EMPTY */
4016 goto empty_content;
4017 case XSLT_FUNC_TEXT:
4018 /* <!-- Content: #PCDATA --> */
4019 goto text;
4020 case XSLT_FUNC_VALUEOF:
4021 /* EMPTY */
4022 goto empty_content;
4023 case XSLT_FUNC_VARIABLE:
4025 * Check for redefinition.
4027 if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
4028 xsltVarInfoPtr ivar = cctxt->ivar;
4030 do {
4031 if ((ivar->name ==
4032 ((xsltStyleItemVariablePtr) elem->psvi)->name) &&
4033 (ivar->nsName ==
4034 ((xsltStyleItemVariablePtr) elem->psvi)->ns))
4036 elem->psvi = NULL;
4037 xsltTransformError(NULL, cctxt->style, elem,
4038 "Redefinition of variable or parameter '%s'.\n",
4039 ivar->name);
4040 cctxt->style->errors++;
4041 goto error;
4043 ivar = ivar->prev;
4044 } while (ivar != NULL);
4046 /* <!-- Content: template --> */
4047 goto sequence_constructor;
4048 case XSLT_FUNC_WHEN:
4049 /* <!-- Content: template --> */
4050 goto sequence_constructor;
4051 case XSLT_FUNC_WITHPARAM:
4052 /* <!-- Content: template --> */
4053 goto sequence_constructor;
4054 default:
4055 #ifdef WITH_XSLT_DEBUG_PARSING
4056 xsltGenericDebug(xsltGenericDebugContext,
4057 "xsltParseXSLTNode: Unhandled XSLT element '%s'.\n",
4058 elem->name);
4059 #endif
4060 xsltTransformError(NULL, cctxt->style, elem,
4061 "xsltParseXSLTNode: Internal error; "
4062 "unhandled XSLT element '%s'.\n", elem->name);
4063 cctxt->style->errors++;
4064 goto internal_err;
4067 apply_templates:
4068 /* <!-- Content: (xsl:sort | xsl:with-param)* --> */
4069 if (elem->children != NULL) {
4070 xmlNodePtr child = elem->children;
4071 do {
4072 if (child->type == XML_ELEMENT_NODE) {
4073 if (IS_XSLT_ELEM_FAST(child)) {
4074 if (xmlStrEqual(child->name, BAD_CAST "with-param")) {
4075 cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM;
4076 xsltParseAnyXSLTElem(cctxt, child);
4077 } else if (xmlStrEqual(child->name, BAD_CAST "sort")) {
4078 cctxt->inode->curChildType = XSLT_FUNC_SORT;
4079 xsltParseAnyXSLTElem(cctxt, child);
4080 } else
4081 xsltParseContentError(cctxt->style, child);
4082 } else
4083 xsltParseContentError(cctxt->style, child);
4085 child = child->next;
4086 } while (child != NULL);
4088 goto exit;
4090 call_template:
4091 /* <!-- Content: xsl:with-param* --> */
4092 if (elem->children != NULL) {
4093 xmlNodePtr child = elem->children;
4094 do {
4095 if (child->type == XML_ELEMENT_NODE) {
4096 if (IS_XSLT_ELEM_FAST(child)) {
4097 xsltStyleType type;
4099 type = xsltGetXSLTElementTypeByNode(cctxt, child);
4100 if (type == XSLT_FUNC_WITHPARAM) {
4101 cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM;
4102 xsltParseAnyXSLTElem(cctxt, child);
4103 } else {
4104 xsltParseContentError(cctxt->style, child);
4106 } else
4107 xsltParseContentError(cctxt->style, child);
4109 child = child->next;
4110 } while (child != NULL);
4112 goto exit;
4114 text:
4115 if (elem->children != NULL) {
4116 xmlNodePtr child = elem->children;
4117 do {
4118 if ((child->type != XML_TEXT_NODE) &&
4119 (child->type != XML_CDATA_SECTION_NODE))
4121 xsltTransformError(NULL, cctxt->style, elem,
4122 "The XSLT 'text' element must have only character "
4123 "data as content.\n");
4125 child = child->next;
4126 } while (child != NULL);
4128 goto exit;
4130 empty_content:
4131 if (elem->children != NULL) {
4132 xmlNodePtr child = elem->children;
4134 * Relaxed behaviour: we will allow whitespace-only text-nodes.
4136 do {
4137 if (((child->type != XML_TEXT_NODE) &&
4138 (child->type != XML_CDATA_SECTION_NODE)) ||
4139 (! IS_BLANK_NODE(child)))
4141 xsltTransformError(NULL, cctxt->style, elem,
4142 "This XSLT element must have no content.\n");
4143 cctxt->style->errors++;
4144 break;
4146 child = child->next;
4147 } while (child != NULL);
4149 goto exit;
4151 choose:
4152 /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */
4154 * TODO: text-nodes in between are *not* allowed in XSLT 1.0.
4155 * The old behaviour did not check this.
4156 * NOTE: In XSLT 2.0 they are stripped beforehand
4157 * if whitespace-only (regardless of xml:space).
4159 if (elem->children != NULL) {
4160 xmlNodePtr child = elem->children;
4161 int nbWhen = 0, nbOtherwise = 0, err = 0;
4162 do {
4163 if (child->type == XML_ELEMENT_NODE) {
4164 if (IS_XSLT_ELEM_FAST(child)) {
4165 xsltStyleType type;
4167 type = xsltGetXSLTElementTypeByNode(cctxt, child);
4168 if (type == XSLT_FUNC_WHEN) {
4169 nbWhen++;
4170 if (nbOtherwise) {
4171 xsltParseContentError(cctxt->style, child);
4172 err = 1;
4173 break;
4175 cctxt->inode->curChildType = XSLT_FUNC_WHEN;
4176 xsltParseAnyXSLTElem(cctxt, child);
4177 } else if (type == XSLT_FUNC_OTHERWISE) {
4178 if (! nbWhen) {
4179 xsltParseContentError(cctxt->style, child);
4180 err = 1;
4181 break;
4183 if (nbOtherwise) {
4184 xsltTransformError(NULL, cctxt->style, elem,
4185 "The XSLT 'choose' element must not contain "
4186 "more than one XSLT 'otherwise' element.\n");
4187 cctxt->style->errors++;
4188 err = 1;
4189 break;
4191 nbOtherwise++;
4192 cctxt->inode->curChildType = XSLT_FUNC_OTHERWISE;
4193 xsltParseAnyXSLTElem(cctxt, child);
4194 } else
4195 xsltParseContentError(cctxt->style, child);
4196 } else
4197 xsltParseContentError(cctxt->style, child);
4200 else
4201 xsltParseContentError(cctxt, child);
4203 child = child->next;
4204 } while (child != NULL);
4205 if ((! err) && (! nbWhen)) {
4206 xsltTransformError(NULL, cctxt->style, elem,
4207 "The XSLT element 'choose' must contain at least one "
4208 "XSLT element 'when'.\n");
4209 cctxt->style->errors++;
4212 goto exit;
4214 for_each:
4215 /* <!-- Content: (xsl:sort*, template) --> */
4217 * NOTE: Text-nodes before xsl:sort are *not* allowed in XSLT 1.0.
4218 * The old behaviour did not allow this, but it catched this
4219 * only at transformation-time.
4220 * In XSLT 2.0 they are stripped beforehand if whitespace-only
4221 * (regardless of xml:space).
4223 if (elem->children != NULL) {
4224 xmlNodePtr child = elem->children;
4226 * Parse xsl:sort first.
4228 do {
4229 if ((child->type == XML_ELEMENT_NODE) &&
4230 IS_XSLT_ELEM_FAST(child))
4232 if (xsltGetXSLTElementTypeByNode(cctxt, child) ==
4233 XSLT_FUNC_SORT)
4235 cctxt->inode->curChildType = XSLT_FUNC_SORT;
4236 xsltParseAnyXSLTElem(cctxt, child);
4237 } else
4238 break;
4239 } else
4240 break;
4241 child = child->next;
4242 } while (child != NULL);
4244 * Parse the sequece constructor.
4246 if (child != NULL)
4247 xsltParseSequenceConstructor(cctxt, child);
4249 goto exit;
4251 sequence_constructor:
4253 * Parse the sequence constructor.
4255 if (elem->children != NULL)
4256 xsltParseSequenceConstructor(cctxt, elem->children);
4259 * Register information for vars/params. Only needed if there
4260 * are any following siblings.
4262 if ((elem->next != NULL) &&
4263 ((cctxt->inode->type == XSLT_FUNC_VARIABLE) ||
4264 (cctxt->inode->type == XSLT_FUNC_PARAM)))
4266 if ((elem->psvi != NULL) &&
4267 (((xsltStyleBasicItemVariablePtr) elem->psvi)->name))
4269 xsltCompilerVarInfoPush(cctxt, elem,
4270 ((xsltStyleBasicItemVariablePtr) elem->psvi)->name,
4271 ((xsltStyleBasicItemVariablePtr) elem->psvi)->ns);
4275 error:
4276 exit:
4277 xsltCompilerNodePop(cctxt, elem);
4278 return(0);
4280 internal_err:
4281 xsltCompilerNodePop(cctxt, elem);
4282 return(-1);
4286 * xsltForwardsCompatUnkownItemCreate:
4288 * @cctxt: the compilation context
4290 * Creates a compiled representation of the unknown
4291 * XSLT instruction.
4293 * Returns the compiled representation.
4295 static xsltStyleItemUknownPtr
4296 xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt)
4298 xsltStyleItemUknownPtr item;
4300 item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown));
4301 if (item == NULL) {
4302 xsltTransformError(NULL, cctxt->style, NULL,
4303 "Internal error in xsltForwardsCompatUnkownItemCreate(): "
4304 "Failed to allocate memory.\n");
4305 cctxt->style->errors++;
4306 return(NULL);
4308 memset(item, 0, sizeof(xsltStyleItemUknown));
4309 item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT;
4311 * Store it in the stylesheet.
4313 item->next = cctxt->style->preComps;
4314 cctxt->style->preComps = (xsltElemPreCompPtr) item;
4315 return(item);
4319 * xsltParseUnknownXSLTElem:
4321 * @cctxt: the compilation context
4322 * @node: the element of the unknown XSLT instruction
4324 * Parses an unknown XSLT element.
4325 * If forwards compatible mode is enabled this will allow
4326 * such an unknown XSLT and; otherwise it is rejected.
4328 * Returns 1 in the unknown XSLT instruction is rejected,
4329 * 0 if everything's fine and
4330 * -1 on API or internal errors.
4332 static int
4333 xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt,
4334 xmlNodePtr node)
4336 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
4337 return(-1);
4340 * Detection of handled content of extension instructions.
4342 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
4343 cctxt->inode->extContentHandled = 1;
4345 if (cctxt->inode->forwardsCompat == 0) {
4347 * We are not in forwards-compatible mode, so raise an error.
4349 xsltTransformError(NULL, cctxt->style, node,
4350 "Unknown XSLT element '%s'.\n", node->name);
4351 cctxt->style->errors++;
4352 return(1);
4355 * Forwards-compatible mode.
4356 * ------------------------
4358 * Parse/compile xsl:fallback elements.
4360 * QUESTION: Do we have to raise an error if there's no xsl:fallback?
4361 * ANSWER: No, since in the stylesheet the fallback behaviour might
4362 * also be provided by using the XSLT function "element-available".
4364 if (cctxt->unknownItem == NULL) {
4366 * Create a singleton for all unknown XSLT instructions.
4368 cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt);
4369 if (cctxt->unknownItem == NULL) {
4370 node->psvi = NULL;
4371 return(-1);
4374 node->psvi = cctxt->unknownItem;
4375 if (node->children == NULL)
4376 return(0);
4377 else {
4378 xmlNodePtr child = node->children;
4380 xsltCompilerNodePush(cctxt, node);
4382 * Update the in-scope namespaces if needed.
4384 if (node->nsDef != NULL)
4385 cctxt->inode->inScopeNs =
4386 xsltCompilerBuildInScopeNsList(cctxt, node);
4388 * Parse all xsl:fallback children.
4390 do {
4391 if ((child->type == XML_ELEMENT_NODE) &&
4392 IS_XSLT_ELEM_FAST(child) &&
4393 IS_XSLT_NAME(child, "fallback"))
4395 cctxt->inode->curChildType = XSLT_FUNC_FALLBACK;
4396 xsltParseAnyXSLTElem(cctxt, child);
4398 child = child->next;
4399 } while (child != NULL);
4401 xsltCompilerNodePop(cctxt, node);
4403 return(0);
4407 * xsltParseSequenceConstructor:
4409 * @cctxt: the compilation context
4410 * @cur: the start-node of the content to be parsed
4412 * Parses a "template" content (or "sequence constructor" in XSLT 2.0 terms).
4413 * This will additionally remove xsl:text elements from the tree.
4415 void
4416 xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
4418 xsltStyleType type;
4419 xmlNodePtr deleteNode = NULL;
4421 if (cctxt == NULL) {
4422 xmlGenericError(xmlGenericErrorContext,
4423 "xsltParseSequenceConstructor: Bad arguments\n");
4424 cctxt->style->errors++;
4425 return;
4428 * Detection of handled content of extension instructions.
4430 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
4431 cctxt->inode->extContentHandled = 1;
4433 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
4434 return;
4436 * This is the content reffered to as a "template".
4437 * E.g. an xsl:element has such content model:
4438 * <xsl:element
4439 * name = { qname }
4440 * namespace = { uri-reference }
4441 * use-attribute-sets = qnames>
4442 * <!-- Content: template -->
4444 * NOTE that in XSLT-2 the term "template" was abandoned due to
4445 * confusion with xsl:template and the term "sequence constructor"
4446 * was introduced instead.
4448 * The following XSLT-instructions are allowed to appear:
4449 * xsl:apply-templates, xsl:call-template, xsl:apply-imports,
4450 * xsl:for-each, xsl:value-of, xsl:copy-of, xsl:number,
4451 * xsl:choose, xsl:if, xsl:text, xsl:copy, xsl:variable,
4452 * xsl:message, xsl:fallback,
4453 * xsl:processing-instruction, xsl:comment, xsl:element
4454 * xsl:attribute.
4455 * Additional allowed content:
4456 * 1) extension instructions
4457 * 2) literal result elements
4458 * 3) PCDATA
4460 * NOTE that this content model does *not* allow xsl:param.
4462 while (cur != NULL) {
4463 if (deleteNode != NULL) {
4464 #ifdef WITH_XSLT_DEBUG_BLANKS
4465 xsltGenericDebug(xsltGenericDebugContext,
4466 "xsltParseSequenceConstructor: removing xsl:text element\n");
4467 #endif
4468 xmlUnlinkNode(deleteNode);
4469 xmlFreeNode(deleteNode);
4470 deleteNode = NULL;
4472 if (cur->type == XML_ELEMENT_NODE) {
4474 if (cur->psvi == xsltXSLTTextMarker) {
4476 * xsl:text elements
4477 * --------------------------------------------------------
4479 xmlNodePtr tmp;
4481 cur->psvi = NULL;
4483 * Mark the xsl:text element for later deletion.
4485 deleteNode = cur;
4487 * Validate content.
4489 tmp = cur->children;
4490 if (tmp) {
4492 * We don't expect more than one text-node in the
4493 * content, since we already merged adjacent
4494 * text/CDATA-nodes and eliminated PI/comment-nodes.
4496 if ((tmp->type == XML_TEXT_NODE) ||
4497 (tmp->next == NULL))
4500 * Leave the contained text-node in the tree.
4502 xmlUnlinkNode(tmp);
4503 xmlAddPrevSibling(cur, tmp);
4504 } else {
4505 tmp = NULL;
4506 xsltTransformError(NULL, cctxt->style, cur,
4507 "Element 'xsl:text': Invalid type "
4508 "of node found in content.\n");
4509 cctxt->style->errors++;
4512 if (cur->properties) {
4513 xmlAttrPtr attr;
4515 * TODO: We need to report errors for
4516 * invalid attrs.
4518 attr = cur->properties;
4519 do {
4520 if ((attr->ns == NULL) &&
4521 (attr->name != NULL) &&
4522 (attr->name[0] == 'd') &&
4523 xmlStrEqual(attr->name,
4524 BAD_CAST "disable-output-escaping"))
4527 * Attr "disable-output-escaping".
4528 * XSLT-2: This attribute is deprecated.
4530 if ((attr->children != NULL) &&
4531 xmlStrEqual(attr->children->content,
4532 BAD_CAST "yes"))
4535 * Disable output escaping for this
4536 * text node.
4538 if (tmp)
4539 tmp->name = xmlStringTextNoenc;
4540 } else if ((attr->children == NULL) ||
4541 (attr->children->content == NULL) ||
4542 (!xmlStrEqual(attr->children->content,
4543 BAD_CAST "no")))
4545 xsltTransformError(NULL, cctxt->style,
4546 cur,
4547 "Attribute 'disable-output-escaping': "
4548 "Invalid value. Expected is "
4549 "'yes' or 'no'.\n");
4550 cctxt->style->errors++;
4552 break;
4554 attr = attr->next;
4555 } while (attr != NULL);
4557 } else if (IS_XSLT_ELEM_FAST(cur)) {
4559 * TODO: Using the XSLT-marker is still not stable yet.
4561 /* if (cur->psvi == xsltXSLTElemMarker) { */
4563 * XSLT instructions
4564 * --------------------------------------------------------
4566 cur->psvi = NULL;
4567 type = xsltGetXSLTElementTypeByNode(cctxt, cur);
4568 switch (type) {
4569 case XSLT_FUNC_APPLYIMPORTS:
4570 case XSLT_FUNC_APPLYTEMPLATES:
4571 case XSLT_FUNC_ATTRIBUTE:
4572 case XSLT_FUNC_CALLTEMPLATE:
4573 case XSLT_FUNC_CHOOSE:
4574 case XSLT_FUNC_COMMENT:
4575 case XSLT_FUNC_COPY:
4576 case XSLT_FUNC_COPYOF:
4577 case XSLT_FUNC_DOCUMENT: /* Extra one */
4578 case XSLT_FUNC_ELEMENT:
4579 case XSLT_FUNC_FALLBACK:
4580 case XSLT_FUNC_FOREACH:
4581 case XSLT_FUNC_IF:
4582 case XSLT_FUNC_MESSAGE:
4583 case XSLT_FUNC_NUMBER:
4584 case XSLT_FUNC_PI:
4585 case XSLT_FUNC_TEXT:
4586 case XSLT_FUNC_VALUEOF:
4587 case XSLT_FUNC_VARIABLE:
4589 * Parse the XSLT element.
4591 cctxt->inode->curChildType = type;
4592 xsltParseAnyXSLTElem(cctxt, cur);
4593 break;
4594 default:
4595 xsltParseUnknownXSLTElem(cctxt, cur);
4596 cur = cur->next;
4597 continue;
4599 } else {
4601 * Non-XSLT elements
4602 * -----------------
4604 xsltCompilerNodePush(cctxt, cur);
4606 * Update the in-scope namespaces if needed.
4608 if (cur->nsDef != NULL)
4609 cctxt->inode->inScopeNs =
4610 xsltCompilerBuildInScopeNsList(cctxt, cur);
4612 * The current element is either a literal result element
4613 * or an extension instruction.
4615 * Process attr "xsl:extension-element-prefixes".
4616 * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be
4617 * processed by the implementor of the extension function;
4618 * i.e., it won't be handled by the XSLT processor.
4620 /* SPEC 1.0:
4621 * "exclude-result-prefixes" is only allowed on literal
4622 * result elements and "xsl:exclude-result-prefixes"
4623 * on xsl:stylesheet/xsl:transform.
4624 * SPEC 2.0:
4625 * "There are a number of standard attributes
4626 * that may appear on any XSLT element: specifically
4627 * version, exclude-result-prefixes,
4628 * extension-element-prefixes, xpath-default-namespace,
4629 * default-collation, and use-when."
4631 * SPEC 2.0:
4632 * For literal result elements:
4633 * "xsl:version, xsl:exclude-result-prefixes,
4634 * xsl:extension-element-prefixes,
4635 * xsl:xpath-default-namespace,
4636 * xsl:default-collation, or xsl:use-when."
4638 if (cur->properties)
4639 cctxt->inode->extElemNs =
4640 xsltParseExtElemPrefixes(cctxt,
4641 cur, cctxt->inode->extElemNs,
4642 XSLT_ELEMENT_CATEGORY_LRE);
4644 * Eval if we have an extension instruction here.
4646 if ((cur->ns != NULL) &&
4647 (cctxt->inode->extElemNs != NULL) &&
4648 (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1))
4651 * Extension instructions
4652 * ----------------------------------------------------
4653 * Mark the node information.
4655 cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION;
4656 cctxt->inode->extContentHandled = 0;
4657 if (cur->psvi != NULL) {
4658 cur->psvi = NULL;
4660 * TODO: Temporary sanity check.
4662 xsltTransformError(NULL, cctxt->style, cur,
4663 "Internal error in xsltParseSequenceConstructor(): "
4664 "Occupied PSVI field.\n");
4665 cctxt->style->errors++;
4666 cur = cur->next;
4667 continue;
4669 cur->psvi = (void *)
4670 xsltPreComputeExtModuleElement(cctxt->style, cur);
4672 if (cur->psvi == NULL) {
4674 * OLD COMMENT: "Unknown element, maybe registered
4675 * at the context level. Mark it for later
4676 * recognition."
4677 * QUESTION: What does the xsltExtMarker mean?
4678 * ANSWER: It is used in
4679 * xsltApplySequenceConstructor() at
4680 * transformation-time to look out for extension
4681 * registered in the transformation context.
4683 cur->psvi = (void *) xsltExtMarker;
4686 * BIG NOTE: Now the ugly part. In previous versions
4687 * of Libxslt (until 1.1.16), all the content of an
4688 * extension instruction was processed and compiled without
4689 * the need of the extension-author to explicitely call
4690 * such a processing;.We now need to mimic this old
4691 * behaviour in order to avoid breaking old code
4692 * on the extension-author's side.
4693 * The mechanism:
4694 * 1) If the author does *not* set the
4695 * compile-time-flag @extContentHandled, then we'll
4696 * parse the content assuming that it's a "template"
4697 * (or "sequence constructor in XSLT 2.0 terms).
4698 * NOTE: If the extension is registered at
4699 * transformation-time only, then there's no way of
4700 * knowing that content shall be valid, and we'll
4701 * process the content the same way.
4702 * 2) If the author *does* set the flag, then we'll assume
4703 * that the author has handled the parsing him/herself
4704 * (e.g. called xsltParseSequenceConstructor(), etc.
4705 * explicitely in his/her code).
4707 if ((cur->children != NULL) &&
4708 (cctxt->inode->extContentHandled == 0))
4711 * Default parsing of the content using the
4712 * sequence-constructor model.
4714 xsltParseSequenceConstructor(cctxt, cur->children);
4716 } else {
4718 * Literal result element
4719 * ----------------------------------------------------
4720 * Allowed XSLT attributes:
4721 * xsl:extension-element-prefixes CDATA #IMPLIED
4722 * xsl:exclude-result-prefixes CDATA #IMPLIED
4723 * TODO: xsl:use-attribute-sets %qnames; #IMPLIED
4724 * xsl:version NMTOKEN #IMPLIED
4726 cur->psvi = NULL;
4727 cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE;
4728 if (cur->properties != NULL) {
4729 xmlAttrPtr attr = cur->properties;
4731 * Attribute "xsl:exclude-result-prefixes".
4733 cctxt->inode->exclResultNs =
4734 xsltParseExclResultPrefixes(cctxt, cur,
4735 cctxt->inode->exclResultNs,
4736 XSLT_ELEMENT_CATEGORY_LRE);
4738 * Attribute "xsl:version".
4740 xsltParseAttrXSLTVersion(cctxt, cur,
4741 XSLT_ELEMENT_CATEGORY_LRE);
4743 * Report invalid XSLT attributes.
4744 * For XSLT 1.0 only xsl:use-attribute-sets is allowed
4745 * next to xsl:version, xsl:exclude-result-prefixes and
4746 * xsl:extension-element-prefixes.
4748 * Mark all XSLT attributes, in order to skip such
4749 * attributes when instantiating the LRE.
4751 do {
4752 if ((attr->psvi != xsltXSLTAttrMarker) &&
4753 IS_XSLT_ATTR_FAST(attr))
4755 if (! xmlStrEqual(attr->name,
4756 BAD_CAST "use-attribute-sets"))
4758 xsltTransformError(NULL, cctxt->style,
4759 cur,
4760 "Unknown XSLT attribute '%s'.\n",
4761 attr->name);
4762 cctxt->style->errors++;
4763 } else {
4765 * XSLT attr marker.
4767 attr->psvi = (void *) xsltXSLTAttrMarker;
4770 attr = attr->next;
4771 } while (attr != NULL);
4774 * Create/reuse info for the literal result element.
4776 if (cctxt->inode->nsChanged)
4777 xsltLREInfoCreate(cctxt, cur, 1);
4778 cur->psvi = cctxt->inode->litResElemInfo;
4780 * Apply ns-aliasing on the element and on its attributes.
4782 if (cctxt->hasNsAliases)
4783 xsltLREBuildEffectiveNs(cctxt, cur);
4785 * Compile attribute value templates (AVT).
4787 if (cur->properties) {
4788 xmlAttrPtr attr = cur->properties;
4790 while (attr != NULL) {
4791 xsltCompileAttr(cctxt->style, attr);
4792 attr = attr->next;
4796 * Parse the content, which is defined to be a "template"
4797 * (or "sequence constructor" in XSLT 2.0 terms).
4799 if (cur->children != NULL) {
4800 xsltParseSequenceConstructor(cctxt, cur->children);
4804 * Leave the non-XSLT element.
4806 xsltCompilerNodePop(cctxt, cur);
4809 cur = cur->next;
4811 if (deleteNode != NULL) {
4812 #ifdef WITH_XSLT_DEBUG_BLANKS
4813 xsltGenericDebug(xsltGenericDebugContext,
4814 "xsltParseSequenceConstructor: removing xsl:text element\n");
4815 #endif
4816 xmlUnlinkNode(deleteNode);
4817 xmlFreeNode(deleteNode);
4818 deleteNode = NULL;
4823 * xsltParseTemplateContent:
4824 * @style: the XSLT stylesheet
4825 * @templ: the node containing the content to be parsed
4827 * Parses and compiles the content-model of an xsl:template element.
4828 * Note that this is *not* the "template" content model (or "sequence
4829 * constructor" in XSLT 2.0); it it allows addional xsl:param
4830 * elements as immediate children of @templ.
4832 * Called by:
4833 * exsltFuncFunctionComp() (EXSLT, functions.c)
4834 * So this is intended to be called from extension functions.
4836 void
4837 xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) {
4838 if ((style == NULL) || (templ == NULL) ||
4839 (templ->type == XML_NAMESPACE_DECL))
4840 return;
4843 * Detection of handled content of extension instructions.
4845 if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
4846 XSLT_CCTXT(style)->inode->extContentHandled = 1;
4849 if (templ->children != NULL) {
4850 xmlNodePtr child = templ->children;
4852 * Process xsl:param elements, which can only occur as the
4853 * immediate children of xsl:template (well, and of any
4854 * user-defined extension instruction if needed).
4856 do {
4857 if ((child->type == XML_ELEMENT_NODE) &&
4858 IS_XSLT_ELEM_FAST(child) &&
4859 IS_XSLT_NAME(child, "param"))
4861 XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM;
4862 xsltParseAnyXSLTElem(XSLT_CCTXT(style), child);
4863 } else
4864 break;
4865 child = child->next;
4866 } while (child != NULL);
4868 * Parse the content and register the pattern.
4870 xsltParseSequenceConstructor(XSLT_CCTXT(style), child);
4874 #else /* XSLT_REFACTORED */
4877 * xsltParseTemplateContent:
4878 * @style: the XSLT stylesheet
4879 * @templ: the container node (can be a document for literal results)
4881 * parse a template content-model
4882 * Clean-up the template content from unwanted ignorable blank nodes
4883 * and process xslt:text
4885 void
4886 xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) {
4887 xmlNodePtr cur, delete;
4889 if ((style == NULL) || (templ == NULL) ||
4890 (templ->type == XML_NAMESPACE_DECL)) return;
4893 * This content comes from the stylesheet
4894 * For stylesheets, the set of whitespace-preserving
4895 * element names consists of just xsl:text.
4897 cur = templ->children;
4898 delete = NULL;
4899 while (cur != NULL) {
4900 if (delete != NULL) {
4901 #ifdef WITH_XSLT_DEBUG_BLANKS
4902 xsltGenericDebug(xsltGenericDebugContext,
4903 "xsltParseTemplateContent: removing text\n");
4904 #endif
4905 xmlUnlinkNode(delete);
4906 xmlFreeNode(delete);
4907 delete = NULL;
4909 if (IS_XSLT_ELEM(cur)) {
4910 xsltStylePreCompute(style, cur);
4912 if (IS_XSLT_NAME(cur, "text")) {
4914 * TODO: Processing of xsl:text should be moved to
4915 * xsltPreprocessStylesheet(), since otherwise this
4916 * will be performed for every multiply included
4917 * stylesheet; i.e. this here is not skipped with
4918 * the use of the style->nopreproc flag.
4920 if (cur->children != NULL) {
4921 xmlChar *prop;
4922 xmlNodePtr text = cur->children, next;
4923 int noesc = 0;
4925 prop = xmlGetNsProp(cur,
4926 (const xmlChar *)"disable-output-escaping",
4927 NULL);
4928 if (prop != NULL) {
4929 #ifdef WITH_XSLT_DEBUG_PARSING
4930 xsltGenericDebug(xsltGenericDebugContext,
4931 "Disable escaping: %s\n", text->content);
4932 #endif
4933 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
4934 noesc = 1;
4935 } else if (!xmlStrEqual(prop,
4936 (const xmlChar *)"no")){
4937 xsltTransformError(NULL, style, cur,
4938 "xsl:text: disable-output-escaping allows only yes or no\n");
4939 style->warnings++;
4942 xmlFree(prop);
4945 while (text != NULL) {
4946 if (text->type == XML_COMMENT_NODE) {
4947 text = text->next;
4948 continue;
4950 if ((text->type != XML_TEXT_NODE) &&
4951 (text->type != XML_CDATA_SECTION_NODE)) {
4952 xsltTransformError(NULL, style, cur,
4953 "xsltParseTemplateContent: xslt:text content problem\n");
4954 style->errors++;
4955 break;
4957 if ((noesc) && (text->type != XML_CDATA_SECTION_NODE))
4958 text->name = xmlStringTextNoenc;
4959 text = text->next;
4963 * replace xsl:text by the list of childs
4965 if (text == NULL) {
4966 text = cur->children;
4967 while (text != NULL) {
4968 if ((style->internalized) &&
4969 (text->content != NULL) &&
4970 (!xmlDictOwns(style->dict, text->content))) {
4973 * internalize the text string
4975 if (text->doc->dict != NULL) {
4976 const xmlChar *tmp;
4978 tmp = xmlDictLookup(text->doc->dict,
4979 text->content, -1);
4980 if (tmp != text->content) {
4981 xmlNodeSetContent(text, NULL);
4982 text->content = (xmlChar *) tmp;
4987 next = text->next;
4988 xmlUnlinkNode(text);
4989 xmlAddPrevSibling(cur, text);
4990 text = next;
4994 delete = cur;
4995 goto skip_children;
4998 else if ((cur->ns != NULL) && (style->nsDefs != NULL) &&
4999 (xsltCheckExtPrefix(style, cur->ns->prefix)))
5002 * okay this is an extension element compile it too
5004 xsltStylePreCompute(style, cur);
5006 else if (cur->type == XML_ELEMENT_NODE)
5009 * This is an element which will be output as part of the
5010 * template exectution, precompile AVT if found.
5012 if ((cur->ns == NULL) && (style->defaultAlias != NULL)) {
5013 cur->ns = xmlSearchNsByHref(cur->doc, cur,
5014 style->defaultAlias);
5016 if (cur->properties != NULL) {
5017 xmlAttrPtr attr = cur->properties;
5019 while (attr != NULL) {
5020 xsltCompileAttr(style, attr);
5021 attr = attr->next;
5026 * Skip to next node
5028 if (cur->children != NULL) {
5029 if (cur->children->type != XML_ENTITY_DECL) {
5030 cur = cur->children;
5031 continue;
5034 skip_children:
5035 if (cur->next != NULL) {
5036 cur = cur->next;
5037 continue;
5040 do {
5041 cur = cur->parent;
5042 if (cur == NULL)
5043 break;
5044 if (cur == templ) {
5045 cur = NULL;
5046 break;
5048 if (cur->next != NULL) {
5049 cur = cur->next;
5050 break;
5052 } while (cur != NULL);
5054 if (delete != NULL) {
5055 #ifdef WITH_XSLT_DEBUG_PARSING
5056 xsltGenericDebug(xsltGenericDebugContext,
5057 "xsltParseTemplateContent: removing text\n");
5058 #endif
5059 xmlUnlinkNode(delete);
5060 xmlFreeNode(delete);
5061 delete = NULL;
5065 * Skip the first params
5067 cur = templ->children;
5068 while (cur != NULL) {
5069 if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param"))))
5070 break;
5071 cur = cur->next;
5075 * Browse the remainder of the template
5077 while (cur != NULL) {
5078 if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
5079 xmlNodePtr param = cur;
5081 xsltTransformError(NULL, style, cur,
5082 "xsltParseTemplateContent: ignoring misplaced param element\n");
5083 if (style != NULL) style->warnings++;
5084 cur = cur->next;
5085 xmlUnlinkNode(param);
5086 xmlFreeNode(param);
5087 } else
5088 break;
5092 #endif /* else XSLT_REFACTORED */
5095 * xsltParseStylesheetKey:
5096 * @style: the XSLT stylesheet
5097 * @key: the "key" element
5099 * <!-- Category: top-level-element -->
5100 * <xsl:key name = qname, match = pattern, use = expression />
5102 * parse an XSLT stylesheet key definition and register it
5105 static void
5106 xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) {
5107 xmlChar *prop = NULL;
5108 xmlChar *use = NULL;
5109 xmlChar *match = NULL;
5110 xmlChar *name = NULL;
5111 xmlChar *nameURI = NULL;
5113 if ((style == NULL) || (key == NULL) || (key->type != XML_ELEMENT_NODE))
5114 return;
5117 * Get arguments
5119 prop = xmlGetNsProp(key, (const xmlChar *)"name", NULL);
5120 if (prop != NULL) {
5121 const xmlChar *URI;
5124 * TODO: Don't use xsltGetQNameURI().
5126 URI = xsltGetQNameURI(key, &prop);
5127 if (prop == NULL) {
5128 if (style != NULL) style->errors++;
5129 goto error;
5130 } else {
5131 name = prop;
5132 if (URI != NULL)
5133 nameURI = xmlStrdup(URI);
5135 #ifdef WITH_XSLT_DEBUG_PARSING
5136 xsltGenericDebug(xsltGenericDebugContext,
5137 "xsltParseStylesheetKey: name %s\n", name);
5138 #endif
5139 } else {
5140 xsltTransformError(NULL, style, key,
5141 "xsl:key : error missing name\n");
5142 if (style != NULL) style->errors++;
5143 goto error;
5146 match = xmlGetNsProp(key, (const xmlChar *)"match", NULL);
5147 if (match == NULL) {
5148 xsltTransformError(NULL, style, key,
5149 "xsl:key : error missing match\n");
5150 if (style != NULL) style->errors++;
5151 goto error;
5154 use = xmlGetNsProp(key, (const xmlChar *)"use", NULL);
5155 if (use == NULL) {
5156 xsltTransformError(NULL, style, key,
5157 "xsl:key : error missing use\n");
5158 if (style != NULL) style->errors++;
5159 goto error;
5163 * register the keys
5165 xsltAddKey(style, name, nameURI, match, use, key);
5168 error:
5169 if (use != NULL)
5170 xmlFree(use);
5171 if (match != NULL)
5172 xmlFree(match);
5173 if (name != NULL)
5174 xmlFree(name);
5175 if (nameURI != NULL)
5176 xmlFree(nameURI);
5178 if (key->children != NULL) {
5179 xsltParseContentError(style, key->children);
5183 #ifdef XSLT_REFACTORED
5185 * xsltParseXSLTTemplate:
5186 * @style: the XSLT stylesheet
5187 * @template: the "template" element
5189 * parse an XSLT stylesheet template building the associated structures
5190 * TODO: Is @style ever expected to be NULL?
5192 * Called from:
5193 * xsltParseXSLTStylesheet()
5194 * xsltParseStylesheetTop()
5197 static void
5198 xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) {
5199 xsltTemplatePtr templ;
5200 xmlChar *prop;
5201 double priority;
5203 if ((cctxt == NULL) || (templNode == NULL) ||
5204 (templNode->type != XML_ELEMENT_NODE))
5205 return;
5208 * Create and link the structure
5210 templ = xsltNewTemplate();
5211 if (templ == NULL)
5212 return;
5214 xsltCompilerNodePush(cctxt, templNode);
5215 if (templNode->nsDef != NULL)
5216 cctxt->inode->inScopeNs =
5217 xsltCompilerBuildInScopeNsList(cctxt, templNode);
5219 templ->next = cctxt->style->templates;
5220 cctxt->style->templates = templ;
5221 templ->style = cctxt->style;
5224 * Attribute "mode".
5226 prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL);
5227 if (prop != NULL) {
5228 const xmlChar *modeURI;
5231 * TODO: We need a standardized function for extraction
5232 * of namespace names and local names from QNames.
5233 * Don't use xsltGetQNameURI() as it cannot channe�
5234 * reports through the context.
5236 modeURI = xsltGetQNameURI(templNode, &prop);
5237 if (prop == NULL) {
5238 cctxt->style->errors++;
5239 goto error;
5241 templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1);
5242 xmlFree(prop);
5243 prop = NULL;
5244 if (xmlValidateNCName(templ->mode, 0)) {
5245 xsltTransformError(NULL, cctxt->style, templNode,
5246 "xsl:template: Attribute 'mode': The local part '%s' "
5247 "of the value is not a valid NCName.\n", templ->name);
5248 cctxt->style->errors++;
5249 goto error;
5251 if (modeURI != NULL)
5252 templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1);
5253 #ifdef WITH_XSLT_DEBUG_PARSING
5254 xsltGenericDebug(xsltGenericDebugContext,
5255 "xsltParseXSLTTemplate: mode %s\n", templ->mode);
5256 #endif
5259 * Attribute "match".
5261 prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL);
5262 if (prop != NULL) {
5263 templ->match = prop;
5264 prop = NULL;
5267 * Attribute "priority".
5269 prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL);
5270 if (prop != NULL) {
5271 priority = xmlXPathStringEvalNumber(prop);
5272 templ->priority = (float) priority;
5273 xmlFree(prop);
5274 prop = NULL;
5277 * Attribute "name".
5279 prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL);
5280 if (prop != NULL) {
5281 const xmlChar *nameURI;
5282 xsltTemplatePtr curTempl;
5285 * TODO: Don't use xsltGetQNameURI().
5287 nameURI = xsltGetQNameURI(templNode, &prop);
5288 if (prop == NULL) {
5289 cctxt->style->errors++;
5290 goto error;
5292 templ->name = xmlDictLookup(cctxt->style->dict, prop, -1);
5293 xmlFree(prop);
5294 prop = NULL;
5295 if (xmlValidateNCName(templ->name, 0)) {
5296 xsltTransformError(NULL, cctxt->style, templNode,
5297 "xsl:template: Attribute 'name': The local part '%s' of "
5298 "the value is not a valid NCName.\n", templ->name);
5299 cctxt->style->errors++;
5300 goto error;
5302 if (nameURI != NULL)
5303 templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1);
5304 curTempl = templ->next;
5305 while (curTempl != NULL) {
5306 if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) &&
5307 xmlStrEqual(curTempl->nameURI, nameURI) ) ||
5308 (nameURI == NULL && curTempl->nameURI == NULL &&
5309 xmlStrEqual(curTempl->name, templ->name)))
5311 xsltTransformError(NULL, cctxt->style, templNode,
5312 "xsl:template: error duplicate name '%s'\n", templ->name);
5313 cctxt->style->errors++;
5314 goto error;
5316 curTempl = curTempl->next;
5319 if (templNode->children != NULL) {
5320 xsltParseTemplateContent(cctxt->style, templNode);
5322 * MAYBE TODO: Custom behaviour: In order to stay compatible with
5323 * Xalan and MSXML(.NET), we could allow whitespace
5324 * to appear before an xml:param element; this whitespace
5325 * will additionally become part of the "template".
5326 * NOTE that this is totally deviates from the spec, but
5327 * is the de facto behaviour of Xalan and MSXML(.NET).
5328 * Personally I wouldn't allow this, since if we have:
5329 * <xsl:template ...xml:space="preserve">
5330 * <xsl:param name="foo"/>
5331 * <xsl:param name="bar"/>
5332 * <xsl:param name="zoo"/>
5333 * ... the whitespace between every xsl:param would be
5334 * added to the result tree.
5338 templ->elem = templNode;
5339 templ->content = templNode->children;
5340 xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI);
5342 error:
5343 xsltCompilerNodePop(cctxt, templNode);
5344 return;
5347 #else /* XSLT_REFACTORED */
5350 * xsltParseStylesheetTemplate:
5351 * @style: the XSLT stylesheet
5352 * @template: the "template" element
5354 * parse an XSLT stylesheet template building the associated structures
5357 static void
5358 xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) {
5359 xsltTemplatePtr ret;
5360 xmlChar *prop;
5361 xmlChar *mode = NULL;
5362 xmlChar *modeURI = NULL;
5363 double priority;
5365 if ((style == NULL) || (template == NULL) ||
5366 (template->type != XML_ELEMENT_NODE))
5367 return;
5370 * Create and link the structure
5372 ret = xsltNewTemplate();
5373 if (ret == NULL)
5374 return;
5375 ret->next = style->templates;
5376 style->templates = ret;
5377 ret->style = style;
5380 * Get inherited namespaces
5383 * TODO: Apply the optimized in-scope-namespace mechanism
5384 * as for the other XSLT instructions.
5386 xsltGetInheritedNsList(style, ret, template);
5389 * Get arguments
5391 prop = xmlGetNsProp(template, (const xmlChar *)"mode", NULL);
5392 if (prop != NULL) {
5393 const xmlChar *URI;
5396 * TODO: Don't use xsltGetQNameURI().
5398 URI = xsltGetQNameURI(template, &prop);
5399 if (prop == NULL) {
5400 if (style != NULL) style->errors++;
5401 goto error;
5402 } else {
5403 mode = prop;
5404 if (URI != NULL)
5405 modeURI = xmlStrdup(URI);
5407 ret->mode = xmlDictLookup(style->dict, mode, -1);
5408 ret->modeURI = xmlDictLookup(style->dict, modeURI, -1);
5409 #ifdef WITH_XSLT_DEBUG_PARSING
5410 xsltGenericDebug(xsltGenericDebugContext,
5411 "xsltParseStylesheetTemplate: mode %s\n", mode);
5412 #endif
5413 if (mode != NULL) xmlFree(mode);
5414 if (modeURI != NULL) xmlFree(modeURI);
5416 prop = xmlGetNsProp(template, (const xmlChar *)"match", NULL);
5417 if (prop != NULL) {
5418 if (ret->match != NULL) xmlFree(ret->match);
5419 ret->match = prop;
5422 prop = xmlGetNsProp(template, (const xmlChar *)"priority", NULL);
5423 if (prop != NULL) {
5424 priority = xmlXPathStringEvalNumber(prop);
5425 ret->priority = (float) priority;
5426 xmlFree(prop);
5429 prop = xmlGetNsProp(template, (const xmlChar *)"name", NULL);
5430 if (prop != NULL) {
5431 const xmlChar *URI;
5434 * TODO: Don't use xsltGetQNameURI().
5436 URI = xsltGetQNameURI(template, &prop);
5437 if (prop == NULL) {
5438 if (style != NULL) style->errors++;
5439 goto error;
5440 } else {
5441 if (xmlValidateNCName(prop,0)) {
5442 xsltTransformError(NULL, style, template,
5443 "xsl:template : error invalid name '%s'\n", prop);
5444 if (style != NULL) style->errors++;
5445 xmlFree(prop);
5446 goto error;
5448 ret->name = xmlDictLookup(style->dict, BAD_CAST prop, -1);
5449 xmlFree(prop);
5450 prop = NULL;
5451 if (URI != NULL)
5452 ret->nameURI = xmlDictLookup(style->dict, BAD_CAST URI, -1);
5453 else
5454 ret->nameURI = NULL;
5459 * parse the content and register the pattern
5461 xsltParseTemplateContent(style, template);
5462 ret->elem = template;
5463 ret->content = template->children;
5464 xsltAddTemplate(style, ret, ret->mode, ret->modeURI);
5466 error:
5467 return;
5470 #endif /* else XSLT_REFACTORED */
5472 #ifdef XSLT_REFACTORED
5475 * xsltIncludeComp:
5476 * @cctxt: the compilation context
5477 * @node: the xsl:include node
5479 * Process the xslt include node on the source node
5481 static xsltStyleItemIncludePtr
5482 xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) {
5483 xsltStyleItemIncludePtr item;
5485 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
5486 return(NULL);
5488 node->psvi = NULL;
5489 item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude));
5490 if (item == NULL) {
5491 xsltTransformError(NULL, cctxt->style, node,
5492 "xsltIncludeComp : malloc failed\n");
5493 cctxt->style->errors++;
5494 return(NULL);
5496 memset(item, 0, sizeof(xsltStyleItemInclude));
5498 node->psvi = item;
5499 item->inst = node;
5500 item->type = XSLT_FUNC_INCLUDE;
5502 item->next = cctxt->style->preComps;
5503 cctxt->style->preComps = (xsltElemPreCompPtr) item;
5505 return(item);
5508 static int
5509 xsltParseFindTopLevelElem(xsltCompilerCtxtPtr cctxt,
5510 xmlNodePtr cur,
5511 const xmlChar *name,
5512 const xmlChar *namespaceURI,
5513 int breakOnOtherElem,
5514 xmlNodePtr *resultNode)
5516 if (name == NULL)
5517 return(-1);
5519 *resultNode = NULL;
5520 while (cur != NULL) {
5521 if (cur->type == XML_ELEMENT_NODE) {
5522 if ((cur->ns != NULL) && (cur->name != NULL)) {
5523 if ((*(cur->name) == *name) &&
5524 xmlStrEqual(cur->name, name) &&
5525 xmlStrEqual(cur->ns->href, namespaceURI))
5527 *resultNode = cur;
5528 return(1);
5531 if (breakOnOtherElem)
5532 break;
5534 cur = cur->next;
5536 *resultNode = cur;
5537 return(0);
5540 static int
5541 xsltParseTopLevelXSLTElem(xsltCompilerCtxtPtr cctxt,
5542 xmlNodePtr node,
5543 xsltStyleType type)
5545 int ret = 0;
5548 * TODO: The reason why this function exists:
5549 * due to historical reasons some of the
5550 * top-level declarations are processed by functions
5551 * in other files. Since we need still to set
5552 * up the node-info and generate information like
5553 * in-scope namespaces, this is a wrapper around
5554 * those old parsing functions.
5556 xsltCompilerNodePush(cctxt, node);
5557 if (node->nsDef != NULL)
5558 cctxt->inode->inScopeNs =
5559 xsltCompilerBuildInScopeNsList(cctxt, node);
5560 cctxt->inode->type = type;
5562 switch (type) {
5563 case XSLT_FUNC_INCLUDE:
5565 int oldIsInclude;
5567 if (xsltCompileXSLTIncludeElem(cctxt, node) == NULL)
5568 goto exit;
5570 * Mark this stylesheet tree as being currently included.
5572 oldIsInclude = cctxt->isInclude;
5573 cctxt->isInclude = 1;
5575 if (xsltParseStylesheetInclude(cctxt->style, node) != 0) {
5576 cctxt->style->errors++;
5578 cctxt->isInclude = oldIsInclude;
5580 break;
5581 case XSLT_FUNC_PARAM:
5582 xsltStylePreCompute(cctxt->style, node);
5583 xsltParseGlobalParam(cctxt->style, node);
5584 break;
5585 case XSLT_FUNC_VARIABLE:
5586 xsltStylePreCompute(cctxt->style, node);
5587 xsltParseGlobalVariable(cctxt->style, node);
5588 break;
5589 case XSLT_FUNC_ATTRSET:
5590 xsltParseStylesheetAttributeSet(cctxt->style, node);
5591 break;
5592 default:
5593 xsltTransformError(NULL, cctxt->style, node,
5594 "Internal error: (xsltParseTopLevelXSLTElem) "
5595 "Cannot handle this top-level declaration.\n");
5596 cctxt->style->errors++;
5597 ret = -1;
5600 exit:
5601 xsltCompilerNodePop(cctxt, node);
5603 return(ret);
5606 #if 0
5607 static int
5608 xsltParseRemoveWhitespace(xmlNodePtr node)
5610 if ((node == NULL) || (node->children == NULL))
5611 return(0);
5612 else {
5613 xmlNodePtr delNode = NULL, child = node->children;
5615 do {
5616 if (delNode) {
5617 xmlUnlinkNode(delNode);
5618 xmlFreeNode(delNode);
5619 delNode = NULL;
5621 if (((child->type == XML_TEXT_NODE) ||
5622 (child->type == XML_CDATA_SECTION_NODE)) &&
5623 (IS_BLANK_NODE(child)))
5624 delNode = child;
5625 child = child->next;
5626 } while (child != NULL);
5627 if (delNode) {
5628 xmlUnlinkNode(delNode);
5629 xmlFreeNode(delNode);
5630 delNode = NULL;
5633 return(0);
5635 #endif
5637 static int
5638 xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
5640 #ifdef WITH_XSLT_DEBUG_PARSING
5641 int templates = 0;
5642 #endif
5643 xmlNodePtr cur, start = NULL;
5644 xsltStylesheetPtr style;
5646 if ((cctxt == NULL) || (node == NULL) ||
5647 (node->type != XML_ELEMENT_NODE))
5648 return(-1);
5650 style = cctxt->style;
5652 * At this stage all import declarations of all stylesheet modules
5653 * with the same stylesheet level have been processed.
5654 * Now we can safely parse the rest of the declarations.
5656 if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include"))
5658 xsltDocumentPtr include;
5660 * URGENT TODO: Make this work with simplified stylesheets!
5661 * I.e., when we won't find an xsl:stylesheet element.
5664 * This is as include declaration.
5666 include = ((xsltStyleItemIncludePtr) node->psvi)->include;
5667 if (include == NULL) {
5668 /* TODO: raise error? */
5669 return(-1);
5672 * TODO: Actually an xsl:include should locate an embedded
5673 * stylesheet as well; so the document-element won't always
5674 * be the element where the actual stylesheet is rooted at.
5675 * But such embedded stylesheets are not supported by Libxslt yet.
5677 node = xmlDocGetRootElement(include->doc);
5678 if (node == NULL) {
5679 return(-1);
5683 if (node->children == NULL)
5684 return(0);
5686 * Push the xsl:stylesheet/xsl:transform element.
5688 xsltCompilerNodePush(cctxt, node);
5689 cctxt->inode->isRoot = 1;
5690 cctxt->inode->nsChanged = 0;
5692 * Start with the naked dummy info for literal result elements.
5694 cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo;
5697 * In every case, we need to have
5698 * the in-scope namespaces of the element, where the
5699 * stylesheet is rooted at, regardless if it's an XSLT
5700 * instruction or a literal result instruction (or if
5701 * this is an embedded stylesheet).
5703 cctxt->inode->inScopeNs =
5704 xsltCompilerBuildInScopeNsList(cctxt, node);
5707 * Process attributes of xsl:stylesheet/xsl:transform.
5708 * --------------------------------------------------
5709 * Allowed are:
5710 * id = id
5711 * extension-element-prefixes = tokens
5712 * exclude-result-prefixes = tokens
5713 * version = number (mandatory)
5715 if (xsltParseAttrXSLTVersion(cctxt, node,
5716 XSLT_ELEMENT_CATEGORY_XSLT) == 0)
5719 * Attribute "version".
5720 * XSLT 1.0: "An xsl:stylesheet element *must* have a version
5721 * attribute, indicating the version of XSLT that the
5722 * stylesheet requires".
5723 * The root element of a simplified stylesheet must also have
5724 * this attribute.
5726 #ifdef XSLT_REFACTORED_MANDATORY_VERSION
5727 if (isXsltElem)
5728 xsltTransformError(NULL, cctxt->style, node,
5729 "The attribute 'version' is missing.\n");
5730 cctxt->style->errors++;
5731 #else
5732 /* OLD behaviour. */
5733 xsltTransformError(NULL, cctxt->style, node,
5734 "xsl:version is missing: document may not be a stylesheet\n");
5735 cctxt->style->warnings++;
5736 #endif
5739 * The namespaces declared by the attributes
5740 * "extension-element-prefixes" and
5741 * "exclude-result-prefixes" are local to *this*
5742 * stylesheet tree; i.e., they are *not* visible to
5743 * other stylesheet-modules, whether imported or included.
5745 * Attribute "extension-element-prefixes".
5747 cctxt->inode->extElemNs =
5748 xsltParseExtElemPrefixes(cctxt, node, NULL,
5749 XSLT_ELEMENT_CATEGORY_XSLT);
5751 * Attribute "exclude-result-prefixes".
5753 cctxt->inode->exclResultNs =
5754 xsltParseExclResultPrefixes(cctxt, node, NULL,
5755 XSLT_ELEMENT_CATEGORY_XSLT);
5757 * Create/reuse info for the literal result element.
5759 if (cctxt->inode->nsChanged)
5760 xsltLREInfoCreate(cctxt, node, 0);
5762 * Processed top-level elements:
5763 * ----------------------------
5764 * xsl:variable, xsl:param (QName, in-scope ns,
5765 * expression (vars allowed))
5766 * xsl:attribute-set (QName, in-scope ns)
5767 * xsl:strip-space, xsl:preserve-space (XPath NameTests,
5768 * in-scope ns)
5769 * I *think* global scope, merge with includes
5770 * xsl:output (QName, in-scope ns)
5771 * xsl:key (QName, in-scope ns, pattern,
5772 * expression (vars *not* allowed))
5773 * xsl:decimal-format (QName, needs in-scope ns)
5774 * xsl:namespace-alias (in-scope ns)
5775 * global scope, merge with includes
5776 * xsl:template (last, QName, pattern)
5778 * (whitespace-only text-nodes have *not* been removed
5779 * yet; this will be done in xsltParseSequenceConstructor)
5781 * Report misplaced child-nodes first.
5783 cur = node->children;
5784 while (cur != NULL) {
5785 if (cur->type == XML_TEXT_NODE) {
5786 xsltTransformError(NULL, style, cur,
5787 "Misplaced text node (content: '%s').\n",
5788 (cur->content != NULL) ? cur->content : BAD_CAST "");
5789 style->errors++;
5790 } else if (cur->type != XML_ELEMENT_NODE) {
5791 xsltTransformError(NULL, style, cur, "Misplaced node.\n");
5792 style->errors++;
5794 cur = cur->next;
5797 * Skip xsl:import elements; they have been processed
5798 * already.
5800 cur = node->children;
5801 while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur,
5802 BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1)
5803 cur = cur->next;
5804 if (cur == NULL)
5805 goto exit;
5807 start = cur;
5809 * Process all top-level xsl:param elements.
5811 while ((cur != NULL) &&
5812 xsltParseFindTopLevelElem(cctxt, cur,
5813 BAD_CAST "param", XSLT_NAMESPACE, 0, &cur) == 1)
5815 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_PARAM);
5816 cur = cur->next;
5819 * Process all top-level xsl:variable elements.
5821 cur = start;
5822 while ((cur != NULL) &&
5823 xsltParseFindTopLevelElem(cctxt, cur,
5824 BAD_CAST "variable", XSLT_NAMESPACE, 0, &cur) == 1)
5826 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE);
5827 cur = cur->next;
5830 * Process all the rest of top-level elements.
5832 cur = start;
5833 while (cur != NULL) {
5835 * Process element nodes.
5837 if (cur->type == XML_ELEMENT_NODE) {
5838 if (cur->ns == NULL) {
5839 xsltTransformError(NULL, style, cur,
5840 "Unexpected top-level element in no namespace.\n");
5841 style->errors++;
5842 cur = cur->next;
5843 continue;
5846 * Process all XSLT elements.
5848 if (IS_XSLT_ELEM_FAST(cur)) {
5850 * xsl:import is only allowed at the beginning.
5852 if (IS_XSLT_NAME(cur, "import")) {
5853 xsltTransformError(NULL, style, cur,
5854 "Misplaced xsl:import element.\n");
5855 style->errors++;
5856 cur = cur->next;
5857 continue;
5860 * TODO: Change the return type of the parsing functions
5861 * to int.
5863 if (IS_XSLT_NAME(cur, "template")) {
5864 #ifdef WITH_XSLT_DEBUG_PARSING
5865 templates++;
5866 #endif
5868 * TODO: Is the position of xsl:template in the
5869 * tree significant? If not it would be easier to
5870 * parse them at a later stage.
5872 xsltParseXSLTTemplate(cctxt, cur);
5873 } else if (IS_XSLT_NAME(cur, "variable")) {
5874 /* NOP; done already */
5875 } else if (IS_XSLT_NAME(cur, "param")) {
5876 /* NOP; done already */
5877 } else if (IS_XSLT_NAME(cur, "include")) {
5878 if (cur->psvi != NULL)
5879 xsltParseXSLTStylesheetElemCore(cctxt, cur);
5880 else {
5881 xsltTransformError(NULL, style, cur,
5882 "Internal error: "
5883 "(xsltParseXSLTStylesheetElemCore) "
5884 "The xsl:include element was not compiled.\n");
5885 style->errors++;
5887 } else if (IS_XSLT_NAME(cur, "strip-space")) {
5888 /* No node info needed. */
5889 xsltParseStylesheetStripSpace(style, cur);
5890 } else if (IS_XSLT_NAME(cur, "preserve-space")) {
5891 /* No node info needed. */
5892 xsltParseStylesheetPreserveSpace(style, cur);
5893 } else if (IS_XSLT_NAME(cur, "output")) {
5894 /* No node-info needed. */
5895 xsltParseStylesheetOutput(style, cur);
5896 } else if (IS_XSLT_NAME(cur, "key")) {
5897 /* TODO: node-info needed for expressions ? */
5898 xsltParseStylesheetKey(style, cur);
5899 } else if (IS_XSLT_NAME(cur, "decimal-format")) {
5900 /* No node-info needed. */
5901 xsltParseStylesheetDecimalFormat(style, cur);
5902 } else if (IS_XSLT_NAME(cur, "attribute-set")) {
5903 xsltParseTopLevelXSLTElem(cctxt, cur,
5904 XSLT_FUNC_ATTRSET);
5905 } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
5906 /* NOP; done already */
5907 } else {
5908 if (cctxt->inode->forwardsCompat) {
5910 * Forwards-compatible mode:
5912 * XSLT-1: "if it is a top-level element and
5913 * XSLT 1.0 does not allow such elements as top-level
5914 * elements, then the element must be ignored along
5915 * with its content;"
5918 * TODO: I don't think we should generate a warning.
5920 xsltTransformError(NULL, style, cur,
5921 "Forwards-compatible mode: Ignoring unknown XSLT "
5922 "element '%s'.\n", cur->name);
5923 style->warnings++;
5924 } else {
5925 xsltTransformError(NULL, style, cur,
5926 "Unknown XSLT element '%s'.\n", cur->name);
5927 style->errors++;
5930 } else {
5931 xsltTopLevelFunction function;
5934 * Process non-XSLT elements, which are in a
5935 * non-NULL namespace.
5938 * QUESTION: What does xsltExtModuleTopLevelLookup()
5939 * do exactly?
5941 function = xsltExtModuleTopLevelLookup(cur->name,
5942 cur->ns->href);
5943 if (function != NULL)
5944 function(style, cur);
5945 #ifdef WITH_XSLT_DEBUG_PARSING
5946 xsltGenericDebug(xsltGenericDebugContext,
5947 "xsltParseXSLTStylesheetElemCore : User-defined "
5948 "data element '%s'.\n", cur->name);
5949 #endif
5952 cur = cur->next;
5955 exit:
5957 #ifdef WITH_XSLT_DEBUG_PARSING
5958 xsltGenericDebug(xsltGenericDebugContext,
5959 "### END of parsing top-level elements of doc '%s'.\n",
5960 node->doc->URL);
5961 xsltGenericDebug(xsltGenericDebugContext,
5962 "### Templates: %d\n", templates);
5963 #ifdef XSLT_REFACTORED
5964 xsltGenericDebug(xsltGenericDebugContext,
5965 "### Max inodes: %d\n", cctxt->maxNodeInfos);
5966 xsltGenericDebug(xsltGenericDebugContext,
5967 "### Max LREs : %d\n", cctxt->maxLREs);
5968 #endif /* XSLT_REFACTORED */
5969 #endif /* WITH_XSLT_DEBUG_PARSING */
5971 xsltCompilerNodePop(cctxt, node);
5972 return(0);
5976 * xsltParseXSLTStylesheet:
5977 * @cctxt: the compiler context
5978 * @node: the xsl:stylesheet/xsl:transform element-node
5980 * Parses the xsl:stylesheet and xsl:transform element.
5982 * <xsl:stylesheet
5983 * id = id
5984 * extension-element-prefixes = tokens
5985 * exclude-result-prefixes = tokens
5986 * version = number>
5987 * <!-- Content: (xsl:import*, top-level-elements) -->
5988 * </xsl:stylesheet>
5990 * BIG TODO: The xsl:include stuff.
5992 * Called by xsltParseStylesheetTree()
5994 * Returns 0 on success, a positive result on errors and
5995 * -1 on API or internal errors.
5997 static int
5998 xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
6000 xmlNodePtr cur, start;
6002 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
6003 return(-1);
6005 if (node->children == NULL)
6006 goto exit;
6009 * Process top-level elements:
6010 * xsl:import (must be first)
6011 * xsl:include (this is just a pre-processing)
6013 cur = node->children;
6015 * Process xsl:import elements.
6016 * XSLT 1.0: "The xsl:import element children must precede all
6017 * other element children of an xsl:stylesheet element,
6018 * including any xsl:include element children."
6020 while ((cur != NULL) &&
6021 xsltParseFindTopLevelElem(cctxt, cur,
6022 BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1)
6024 if (xsltParseStylesheetImport(cctxt->style, cur) != 0) {
6025 cctxt->style->errors++;
6027 cur = cur->next;
6029 if (cur == NULL)
6030 goto exit;
6031 start = cur;
6033 * Pre-process all xsl:include elements.
6035 cur = start;
6036 while ((cur != NULL) &&
6037 xsltParseFindTopLevelElem(cctxt, cur,
6038 BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1)
6040 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE);
6041 cur = cur->next;
6044 * Pre-process all xsl:namespace-alias elements.
6045 * URGENT TODO: This won't work correctly: the order of included
6046 * aliases and aliases defined here is significant.
6048 cur = start;
6049 while ((cur != NULL) &&
6050 xsltParseFindTopLevelElem(cctxt, cur,
6051 BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1)
6053 xsltNamespaceAlias(cctxt->style, cur);
6054 cur = cur->next;
6057 if (cctxt->isInclude) {
6059 * If this stylesheet is intended for inclusion, then
6060 * we will process only imports and includes.
6062 goto exit;
6065 * Now parse the rest of the top-level elements.
6067 xsltParseXSLTStylesheetElemCore(cctxt, node);
6068 exit:
6070 return(0);
6073 #else /* XSLT_REFACTORED */
6076 * xsltParseStylesheetTop:
6077 * @style: the XSLT stylesheet
6078 * @top: the top level "stylesheet" or "transform" element
6080 * scan the top level elements of an XSL stylesheet
6082 static void
6083 xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) {
6084 xmlNodePtr cur;
6085 xmlChar *prop;
6086 #ifdef WITH_XSLT_DEBUG_PARSING
6087 int templates = 0;
6088 #endif
6090 if ((top == NULL) || (top->type != XML_ELEMENT_NODE))
6091 return;
6093 prop = xmlGetNsProp(top, (const xmlChar *)"version", NULL);
6094 if (prop == NULL) {
6095 xsltTransformError(NULL, style, top,
6096 "xsl:version is missing: document may not be a stylesheet\n");
6097 if (style != NULL) style->warnings++;
6098 } else {
6099 if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) &&
6100 (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) {
6101 xsltTransformError(NULL, style, top,
6102 "xsl:version: only 1.1 features are supported\n");
6103 if (style != NULL) {
6104 style->forwards_compatible = 1;
6105 style->warnings++;
6108 xmlFree(prop);
6112 * process xsl:import elements
6114 cur = top->children;
6115 while (cur != NULL) {
6116 if (IS_BLANK_NODE(cur)) {
6117 cur = cur->next;
6118 continue;
6120 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "import")) {
6121 if (xsltParseStylesheetImport(style, cur) != 0)
6122 if (style != NULL) style->errors++;
6123 } else
6124 break;
6125 cur = cur->next;
6129 * process other top-level elements
6131 while (cur != NULL) {
6132 if (IS_BLANK_NODE(cur)) {
6133 cur = cur->next;
6134 continue;
6136 if (cur->type == XML_TEXT_NODE) {
6137 if (cur->content != NULL) {
6138 xsltTransformError(NULL, style, cur,
6139 "misplaced text node: '%s'\n", cur->content);
6141 if (style != NULL) style->errors++;
6142 cur = cur->next;
6143 continue;
6145 if ((cur->type == XML_ELEMENT_NODE) && (cur->ns == NULL)) {
6146 xsltGenericError(xsltGenericErrorContext,
6147 "Found a top-level element %s with null namespace URI\n",
6148 cur->name);
6149 if (style != NULL) style->errors++;
6150 cur = cur->next;
6151 continue;
6153 if ((cur->type == XML_ELEMENT_NODE) && (!(IS_XSLT_ELEM(cur)))) {
6154 xsltTopLevelFunction function;
6156 function = xsltExtModuleTopLevelLookup(cur->name,
6157 cur->ns->href);
6158 if (function != NULL)
6159 function(style, cur);
6161 #ifdef WITH_XSLT_DEBUG_PARSING
6162 xsltGenericDebug(xsltGenericDebugContext,
6163 "xsltParseStylesheetTop : found foreign element %s\n",
6164 cur->name);
6165 #endif
6166 cur = cur->next;
6167 continue;
6169 if (IS_XSLT_NAME(cur, "import")) {
6170 xsltTransformError(NULL, style, cur,
6171 "xsltParseStylesheetTop: ignoring misplaced import element\n");
6172 if (style != NULL) style->errors++;
6173 } else if (IS_XSLT_NAME(cur, "include")) {
6174 if (xsltParseStylesheetInclude(style, cur) != 0)
6175 if (style != NULL) style->errors++;
6176 } else if (IS_XSLT_NAME(cur, "strip-space")) {
6177 xsltParseStylesheetStripSpace(style, cur);
6178 } else if (IS_XSLT_NAME(cur, "preserve-space")) {
6179 xsltParseStylesheetPreserveSpace(style, cur);
6180 } else if (IS_XSLT_NAME(cur, "output")) {
6181 xsltParseStylesheetOutput(style, cur);
6182 } else if (IS_XSLT_NAME(cur, "key")) {
6183 xsltParseStylesheetKey(style, cur);
6184 } else if (IS_XSLT_NAME(cur, "decimal-format")) {
6185 xsltParseStylesheetDecimalFormat(style, cur);
6186 } else if (IS_XSLT_NAME(cur, "attribute-set")) {
6187 xsltParseStylesheetAttributeSet(style, cur);
6188 } else if (IS_XSLT_NAME(cur, "variable")) {
6189 xsltParseGlobalVariable(style, cur);
6190 } else if (IS_XSLT_NAME(cur, "param")) {
6191 xsltParseGlobalParam(style, cur);
6192 } else if (IS_XSLT_NAME(cur, "template")) {
6193 #ifdef WITH_XSLT_DEBUG_PARSING
6194 templates++;
6195 #endif
6196 xsltParseStylesheetTemplate(style, cur);
6197 } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
6198 xsltNamespaceAlias(style, cur);
6199 } else {
6200 if ((style != NULL) && (style->forwards_compatible == 0)) {
6201 xsltTransformError(NULL, style, cur,
6202 "xsltParseStylesheetTop: unknown %s element\n",
6203 cur->name);
6204 if (style != NULL) style->errors++;
6207 cur = cur->next;
6209 #ifdef WITH_XSLT_DEBUG_PARSING
6210 xsltGenericDebug(xsltGenericDebugContext,
6211 "parsed %d templates\n", templates);
6212 #endif
6215 #endif /* else of XSLT_REFACTORED */
6217 #ifdef XSLT_REFACTORED
6219 * xsltParseSimplifiedStylesheetTree:
6221 * @style: the stylesheet (TODO: Change this to the compiler context)
6222 * @doc: the document containing the stylesheet.
6223 * @node: the node where the stylesheet is rooted at
6225 * Returns 0 in case of success, a positive result if an error occurred
6226 * and -1 on API and internal errors.
6228 static int
6229 xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt,
6230 xmlDocPtr doc,
6231 xmlNodePtr node)
6233 xsltTemplatePtr templ;
6235 if ((cctxt == NULL) || (node == NULL))
6236 return(-1);
6238 if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE)
6241 * TODO: Adjust report, since this might be an
6242 * embedded stylesheet.
6244 xsltTransformError(NULL, cctxt->style, node,
6245 "The attribute 'xsl:version' is missing; cannot identify "
6246 "this document as an XSLT stylesheet document.\n");
6247 cctxt->style->errors++;
6248 return(1);
6251 #ifdef WITH_XSLT_DEBUG_PARSING
6252 xsltGenericDebug(xsltGenericDebugContext,
6253 "xsltParseSimplifiedStylesheetTree: document is stylesheet\n");
6254 #endif
6257 * Create and link the template
6259 templ = xsltNewTemplate();
6260 if (templ == NULL) {
6261 return(-1);
6263 templ->next = cctxt->style->templates;
6264 cctxt->style->templates = templ;
6265 templ->match = xmlStrdup(BAD_CAST "/");
6268 * Note that we push the document-node in this special case.
6270 xsltCompilerNodePush(cctxt, (xmlNodePtr) doc);
6272 * In every case, we need to have
6273 * the in-scope namespaces of the element, where the
6274 * stylesheet is rooted at, regardless if it's an XSLT
6275 * instruction or a literal result instruction (or if
6276 * this is an embedded stylesheet).
6278 cctxt->inode->inScopeNs =
6279 xsltCompilerBuildInScopeNsList(cctxt, node);
6281 * Parse the content and register the match-pattern.
6283 xsltParseSequenceConstructor(cctxt, node);
6284 xsltCompilerNodePop(cctxt, (xmlNodePtr) doc);
6286 templ->elem = (xmlNodePtr) doc;
6287 templ->content = node;
6288 xsltAddTemplate(cctxt->style, templ, NULL, NULL);
6289 cctxt->style->literal_result = 1;
6290 return(0);
6293 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
6295 * xsltRestoreDocumentNamespaces:
6296 * @ns: map of namespaces
6297 * @doc: the document
6299 * Restore the namespaces for the document
6301 * Returns 0 in case of success, -1 in case of failure
6304 xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc)
6306 if (doc == NULL)
6307 return(-1);
6309 * Revert the changes we have applied to the namespace-URIs of
6310 * ns-decls.
6312 while (ns != NULL) {
6313 if ((ns->doc == doc) && (ns->ns != NULL)) {
6314 ns->ns->href = ns->origNsName;
6315 ns->origNsName = NULL;
6316 ns->ns = NULL;
6318 ns = ns->next;
6320 return(0);
6322 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
6325 * xsltParseStylesheetProcess:
6326 * @style: the XSLT stylesheet (the current stylesheet-level)
6327 * @doc: and xmlDoc parsed XML
6329 * Parses an XSLT stylesheet, adding the associated structures.
6330 * Called by:
6331 * xsltParseStylesheetImportedDoc() (xslt.c)
6332 * xsltParseStylesheetInclude() (imports.c)
6334 * Returns the value of the @style parameter if everything
6335 * went right, NULL if something went amiss.
6337 xsltStylesheetPtr
6338 xsltParseStylesheetProcess(xsltStylesheetPtr style, xmlDocPtr doc)
6340 xsltCompilerCtxtPtr cctxt;
6341 xmlNodePtr cur;
6342 int oldIsSimplifiedStylesheet;
6344 xsltInitGlobals();
6346 if ((style == NULL) || (doc == NULL))
6347 return(NULL);
6349 cctxt = XSLT_CCTXT(style);
6351 cur = xmlDocGetRootElement(doc);
6352 if (cur == NULL) {
6353 xsltTransformError(NULL, style, (xmlNodePtr) doc,
6354 "xsltParseStylesheetProcess : empty stylesheet\n");
6355 return(NULL);
6357 oldIsSimplifiedStylesheet = cctxt->simplified;
6359 if ((IS_XSLT_ELEM(cur)) &&
6360 ((IS_XSLT_NAME(cur, "stylesheet")) ||
6361 (IS_XSLT_NAME(cur, "transform")))) {
6362 #ifdef WITH_XSLT_DEBUG_PARSING
6363 xsltGenericDebug(xsltGenericDebugContext,
6364 "xsltParseStylesheetProcess : found stylesheet\n");
6365 #endif
6366 cctxt->simplified = 0;
6367 style->literal_result = 0;
6368 } else {
6369 cctxt->simplified = 1;
6370 style->literal_result = 1;
6373 * Pre-process the stylesheet if not already done before.
6374 * This will remove PIs and comments, merge adjacent
6375 * text nodes, internalize strings, etc.
6377 if (! style->nopreproc)
6378 xsltParsePreprocessStylesheetTree(cctxt, cur);
6380 * Parse and compile the stylesheet.
6382 if (style->literal_result == 0) {
6383 if (xsltParseXSLTStylesheetElem(cctxt, cur) != 0)
6384 return(NULL);
6385 } else {
6386 if (xsltParseSimplifiedStylesheetTree(cctxt, doc, cur) != 0)
6387 return(NULL);
6390 cctxt->simplified = oldIsSimplifiedStylesheet;
6392 return(style);
6395 #else /* XSLT_REFACTORED */
6398 * xsltParseStylesheetProcess:
6399 * @ret: the XSLT stylesheet (the current stylesheet-level)
6400 * @doc: and xmlDoc parsed XML
6402 * Parses an XSLT stylesheet, adding the associated structures.
6403 * Called by:
6404 * xsltParseStylesheetImportedDoc() (xslt.c)
6405 * xsltParseStylesheetInclude() (imports.c)
6407 * Returns the value of the @style parameter if everything
6408 * went right, NULL if something went amiss.
6410 xsltStylesheetPtr
6411 xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) {
6412 xmlNodePtr cur;
6414 xsltInitGlobals();
6416 if (doc == NULL)
6417 return(NULL);
6418 if (ret == NULL)
6419 return(ret);
6422 * First steps, remove blank nodes,
6423 * locate the xsl:stylesheet element and the
6424 * namespace declaration.
6426 cur = xmlDocGetRootElement(doc);
6427 if (cur == NULL) {
6428 xsltTransformError(NULL, ret, (xmlNodePtr) doc,
6429 "xsltParseStylesheetProcess : empty stylesheet\n");
6430 return(NULL);
6433 if ((IS_XSLT_ELEM(cur)) &&
6434 ((IS_XSLT_NAME(cur, "stylesheet")) ||
6435 (IS_XSLT_NAME(cur, "transform")))) {
6436 #ifdef WITH_XSLT_DEBUG_PARSING
6437 xsltGenericDebug(xsltGenericDebugContext,
6438 "xsltParseStylesheetProcess : found stylesheet\n");
6439 #endif
6440 ret->literal_result = 0;
6441 xsltParseStylesheetExcludePrefix(ret, cur, 1);
6442 xsltParseStylesheetExtPrefix(ret, cur, 1);
6443 } else {
6444 xsltParseStylesheetExcludePrefix(ret, cur, 0);
6445 xsltParseStylesheetExtPrefix(ret, cur, 0);
6446 ret->literal_result = 1;
6448 if (!ret->nopreproc) {
6449 xsltPreprocessStylesheet(ret, cur);
6451 if (ret->literal_result == 0) {
6452 xsltParseStylesheetTop(ret, cur);
6453 } else {
6454 xmlChar *prop;
6455 xsltTemplatePtr template;
6458 * the document itself might be the template, check xsl:version
6460 prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE);
6461 if (prop == NULL) {
6462 xsltTransformError(NULL, ret, cur,
6463 "xsltParseStylesheetProcess : document is not a stylesheet\n");
6464 return(NULL);
6467 #ifdef WITH_XSLT_DEBUG_PARSING
6468 xsltGenericDebug(xsltGenericDebugContext,
6469 "xsltParseStylesheetProcess : document is stylesheet\n");
6470 #endif
6472 if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) &&
6473 (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) {
6474 xsltTransformError(NULL, ret, cur,
6475 "xsl:version: only 1.1 features are supported\n");
6476 ret->forwards_compatible = 1;
6477 ret->warnings++;
6479 xmlFree(prop);
6482 * Create and link the template
6484 template = xsltNewTemplate();
6485 if (template == NULL) {
6486 return(NULL);
6488 template->next = ret->templates;
6489 ret->templates = template;
6490 template->match = xmlStrdup((const xmlChar *)"/");
6493 * parse the content and register the pattern
6495 xsltParseTemplateContent(ret, (xmlNodePtr) doc);
6496 template->elem = (xmlNodePtr) doc;
6497 template->content = doc->children;
6498 xsltAddTemplate(ret, template, NULL, NULL);
6499 ret->literal_result = 1;
6502 return(ret);
6505 #endif /* else of XSLT_REFACTORED */
6508 * xsltParseStylesheetImportedDoc:
6509 * @doc: an xmlDoc parsed XML
6510 * @parentStyle: pointer to the parent stylesheet (if it exists)
6512 * parse an XSLT stylesheet building the associated structures
6513 * except the processing not needed for imported documents.
6515 * Returns a new XSLT stylesheet structure.
6518 xsltStylesheetPtr
6519 xsltParseStylesheetImportedDoc(xmlDocPtr doc,
6520 xsltStylesheetPtr parentStyle) {
6521 xsltStylesheetPtr retStyle;
6523 if (doc == NULL)
6524 return(NULL);
6526 retStyle = xsltNewStylesheetInternal(parentStyle);
6527 if (retStyle == NULL)
6528 return(NULL);
6530 if (xsltParseStylesheetUser(retStyle, doc) != 0) {
6531 xsltFreeStylesheet(retStyle);
6532 return(NULL);
6535 return(retStyle);
6539 * xsltParseStylesheetUser:
6540 * @style: pointer to the stylesheet
6541 * @doc: an xmlDoc parsed XML
6543 * Parse an XSLT stylesheet with a user-provided stylesheet struct.
6545 * Returns 0 if successful, -1 in case of error.
6548 xsltParseStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc) {
6549 if ((style == NULL) || (doc == NULL))
6550 return(-1);
6553 * Adjust the string dict.
6555 if (doc->dict != NULL) {
6556 xmlDictFree(style->dict);
6557 style->dict = doc->dict;
6558 #ifdef WITH_XSLT_DEBUG
6559 xsltGenericDebug(xsltGenericDebugContext,
6560 "reusing dictionary from %s for stylesheet\n",
6561 doc->URL);
6562 #endif
6563 xmlDictReference(style->dict);
6567 * TODO: Eliminate xsltGatherNamespaces(); we must not restrict
6568 * the stylesheet to containt distinct namespace prefixes.
6570 xsltGatherNamespaces(style);
6572 #ifdef XSLT_REFACTORED
6574 xsltCompilerCtxtPtr cctxt;
6575 xsltStylesheetPtr oldCurSheet;
6577 if (style->parent == NULL) {
6578 xsltPrincipalStylesheetDataPtr principalData;
6580 * Create extra data for the principal stylesheet.
6582 principalData = xsltNewPrincipalStylesheetData();
6583 if (principalData == NULL) {
6584 return(-1);
6586 style->principalData = principalData;
6588 * Create the compilation context
6589 * ------------------------------
6590 * (only once; for the principal stylesheet).
6591 * This is currently the only function where the
6592 * compilation context is created.
6594 cctxt = xsltCompilationCtxtCreate(style);
6595 if (cctxt == NULL) {
6596 return(-1);
6598 style->compCtxt = (void *) cctxt;
6599 cctxt->style = style;
6600 cctxt->dict = style->dict;
6601 cctxt->psData = principalData;
6603 * Push initial dummy node info.
6605 cctxt->depth = -1;
6606 xsltCompilerNodePush(cctxt, (xmlNodePtr) doc);
6607 } else {
6609 * Imported stylesheet.
6611 cctxt = style->parent->compCtxt;
6612 style->compCtxt = cctxt;
6615 * Save the old and set the current stylesheet structure in the
6616 * compilation context.
6618 oldCurSheet = cctxt->style;
6619 cctxt->style = style;
6621 style->doc = doc;
6622 xsltParseStylesheetProcess(style, doc);
6624 cctxt->style = oldCurSheet;
6625 if (style->parent == NULL) {
6627 * Pop the initial dummy node info.
6629 xsltCompilerNodePop(cctxt, (xmlNodePtr) doc);
6630 } else {
6632 * Clear the compilation context of imported
6633 * stylesheets.
6634 * TODO: really?
6636 /* style->compCtxt = NULL; */
6639 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
6640 if (style->errors != 0) {
6642 * Restore all changes made to namespace URIs of ns-decls.
6644 if (cctxt->psData->nsMap)
6645 xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc);
6647 #endif
6649 if (style->parent == NULL) {
6650 xsltCompilationCtxtFree(style->compCtxt);
6651 style->compCtxt = NULL;
6655 #else /* XSLT_REFACTORED */
6657 * Old behaviour.
6659 style->doc = doc;
6660 if (xsltParseStylesheetProcess(style, doc) == NULL) {
6661 style->doc = NULL;
6662 return(-1);
6664 #endif /* else of XSLT_REFACTORED */
6666 if (style->parent == NULL)
6667 xsltResolveStylesheetAttributeSet(style);
6669 if (style->errors != 0) {
6671 * Detach the doc from the stylesheet; otherwise the doc
6672 * will be freed in xsltFreeStylesheet().
6674 style->doc = NULL;
6676 * Cleanup the doc if its the main stylesheet.
6678 if (style->parent == NULL)
6679 xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc));
6680 return(-1);
6683 return(0);
6687 * xsltParseStylesheetDoc:
6688 * @doc: an xmlDoc parsed XML
6690 * parse an XSLT stylesheet, building the associated structures. doc
6691 * is kept as a reference within the returned stylesheet, so changes
6692 * to doc after the parsing will be reflected when the stylesheet
6693 * is applied, and the doc is automatically freed when the
6694 * stylesheet is closed.
6696 * Returns a new XSLT stylesheet structure.
6699 xsltStylesheetPtr
6700 xsltParseStylesheetDoc(xmlDocPtr doc) {
6701 xsltInitGlobals();
6703 return(xsltParseStylesheetImportedDoc(doc, NULL));
6707 * xsltParseStylesheetFile:
6708 * @filename: the filename/URL to the stylesheet
6710 * Load and parse an XSLT stylesheet
6712 * Returns a new XSLT stylesheet structure.
6715 xsltStylesheetPtr
6716 xsltParseStylesheetFile(const xmlChar* filename) {
6717 xsltSecurityPrefsPtr sec;
6718 xsltStylesheetPtr ret;
6719 xmlDocPtr doc;
6721 xsltInitGlobals();
6723 if (filename == NULL)
6724 return(NULL);
6726 #ifdef WITH_XSLT_DEBUG_PARSING
6727 xsltGenericDebug(xsltGenericDebugContext,
6728 "xsltParseStylesheetFile : parse %s\n", filename);
6729 #endif
6732 * Security framework check
6734 sec = xsltGetDefaultSecurityPrefs();
6735 if (sec != NULL) {
6736 int res;
6738 res = xsltCheckRead(sec, NULL, filename);
6739 if (res <= 0) {
6740 if (res == 0)
6741 xsltTransformError(NULL, NULL, NULL,
6742 "xsltParseStylesheetFile: read rights for %s denied\n",
6743 filename);
6744 return(NULL);
6748 doc = xsltDocDefaultLoader(filename, NULL, XSLT_PARSE_OPTIONS,
6749 NULL, XSLT_LOAD_START);
6750 if (doc == NULL) {
6751 xsltTransformError(NULL, NULL, NULL,
6752 "xsltParseStylesheetFile : cannot parse %s\n", filename);
6753 return(NULL);
6755 ret = xsltParseStylesheetDoc(doc);
6756 if (ret == NULL) {
6757 xmlFreeDoc(doc);
6758 return(NULL);
6761 return(ret);
6764 /************************************************************************
6766 * Handling of Stylesheet PI *
6768 ************************************************************************/
6770 #define CUR (*cur)
6771 #define SKIP(val) cur += (val)
6772 #define NXT(val) cur[(val)]
6773 #define SKIP_BLANKS \
6774 while (IS_BLANK(CUR)) NEXT
6775 #define NEXT ((*cur) ? cur++ : cur)
6778 * xsltParseStylesheetPI:
6779 * @value: the value of the PI
6781 * This function checks that the type is text/xml and extracts
6782 * the URI-Reference for the stylesheet
6784 * Returns the URI-Reference for the stylesheet or NULL (it need to
6785 * be freed by the caller)
6787 static xmlChar *
6788 xsltParseStylesheetPI(const xmlChar *value) {
6789 const xmlChar *cur;
6790 const xmlChar *start;
6791 xmlChar *val;
6792 xmlChar tmp;
6793 xmlChar *href = NULL;
6794 int isXml = 0;
6796 if (value == NULL)
6797 return(NULL);
6799 cur = value;
6800 while (CUR != 0) {
6801 SKIP_BLANKS;
6802 if ((CUR == 't') && (NXT(1) == 'y') && (NXT(2) == 'p') &&
6803 (NXT(3) == 'e')) {
6804 SKIP(4);
6805 SKIP_BLANKS;
6806 if (CUR != '=')
6807 continue;
6808 NEXT;
6809 if ((CUR != '\'') && (CUR != '"'))
6810 continue;
6811 tmp = CUR;
6812 NEXT;
6813 start = cur;
6814 while ((CUR != 0) && (CUR != tmp))
6815 NEXT;
6816 if (CUR != tmp)
6817 continue;
6818 val = xmlStrndup(start, cur - start);
6819 NEXT;
6820 if (val == NULL)
6821 return(NULL);
6822 if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) &&
6823 (xmlStrcasecmp(val, BAD_CAST "text/xsl")) &&
6824 (xmlStrcasecmp(val, BAD_CAST "application/xslt+xml"))) {
6825 xmlFree(val);
6826 break;
6828 isXml = 1;
6829 xmlFree(val);
6830 } else if ((CUR == 'h') && (NXT(1) == 'r') && (NXT(2) == 'e') &&
6831 (NXT(3) == 'f')) {
6832 SKIP(4);
6833 SKIP_BLANKS;
6834 if (CUR != '=')
6835 continue;
6836 NEXT;
6837 if ((CUR != '\'') && (CUR != '"'))
6838 continue;
6839 tmp = CUR;
6840 NEXT;
6841 start = cur;
6842 while ((CUR != 0) && (CUR != tmp))
6843 NEXT;
6844 if (CUR != tmp)
6845 continue;
6846 if (href == NULL)
6847 href = xmlStrndup(start, cur - start);
6848 NEXT;
6849 } else {
6850 while ((CUR != 0) && (!IS_BLANK(CUR)))
6851 NEXT;
6856 if (!isXml) {
6857 if (href != NULL)
6858 xmlFree(href);
6859 href = NULL;
6861 return(href);
6865 * xsltLoadStylesheetPI:
6866 * @doc: a document to process
6868 * This function tries to locate the stylesheet PI in the given document
6869 * If found, and if contained within the document, it will extract
6870 * that subtree to build the stylesheet to process @doc (doc itself will
6871 * be modified). If found but referencing an external document it will
6872 * attempt to load it and generate a stylesheet from it. In both cases,
6873 * the resulting stylesheet and the document need to be freed once the
6874 * transformation is done.
6876 * Returns a new XSLT stylesheet structure or NULL if not found.
6878 xsltStylesheetPtr
6879 xsltLoadStylesheetPI(xmlDocPtr doc) {
6880 xmlNodePtr child;
6881 xsltStylesheetPtr ret = NULL;
6882 xmlChar *href = NULL;
6883 xmlURIPtr URI;
6885 xsltInitGlobals();
6887 if (doc == NULL)
6888 return(NULL);
6891 * Find the text/xml stylesheet PI id any before the root
6893 child = doc->children;
6894 while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
6895 if ((child->type == XML_PI_NODE) &&
6896 (xmlStrEqual(child->name, BAD_CAST "xml-stylesheet"))) {
6897 href = xsltParseStylesheetPI(child->content);
6898 if (href != NULL)
6899 break;
6901 child = child->next;
6905 * If found check the href to select processing
6907 if (href != NULL) {
6908 #ifdef WITH_XSLT_DEBUG_PARSING
6909 xsltGenericDebug(xsltGenericDebugContext,
6910 "xsltLoadStylesheetPI : found PI href=%s\n", href);
6911 #endif
6912 URI = xmlParseURI((const char *) href);
6913 if (URI == NULL) {
6914 xsltTransformError(NULL, NULL, child,
6915 "xml-stylesheet : href %s is not valid\n", href);
6916 xmlFree(href);
6917 return(NULL);
6919 if ((URI->fragment != NULL) && (URI->scheme == NULL) &&
6920 (URI->opaque == NULL) && (URI->authority == NULL) &&
6921 (URI->server == NULL) && (URI->user == NULL) &&
6922 (URI->path == NULL) && (URI->query == NULL)) {
6923 xmlAttrPtr ID;
6925 #ifdef WITH_XSLT_DEBUG_PARSING
6926 xsltGenericDebug(xsltGenericDebugContext,
6927 "xsltLoadStylesheetPI : Reference to ID %s\n", href);
6928 #endif
6929 if (URI->fragment[0] == '#')
6930 ID = xmlGetID(doc, (const xmlChar *) &(URI->fragment[1]));
6931 else
6932 ID = xmlGetID(doc, (const xmlChar *) URI->fragment);
6933 if (ID == NULL) {
6934 xsltTransformError(NULL, NULL, child,
6935 "xml-stylesheet : no ID %s found\n", URI->fragment);
6936 } else {
6937 xmlDocPtr fake;
6938 xmlNodePtr subtree, newtree;
6939 xmlNsPtr ns;
6941 #ifdef WITH_XSLT_DEBUG
6942 xsltGenericDebug(xsltGenericDebugContext,
6943 "creating new document from %s for embedded stylesheet\n",
6944 doc->URL);
6945 #endif
6947 * move the subtree in a new document passed to
6948 * the stylesheet analyzer
6950 subtree = ID->parent;
6951 fake = xmlNewDoc(NULL);
6952 if (fake != NULL) {
6954 * Should the dictionary still be shared even though
6955 * the nodes are being copied rather than moved?
6957 fake->dict = doc->dict;
6958 xmlDictReference(doc->dict);
6959 #ifdef WITH_XSLT_DEBUG
6960 xsltGenericDebug(xsltGenericDebugContext,
6961 "reusing dictionary from %s for embedded stylesheet\n",
6962 doc->URL);
6963 #endif
6965 newtree = xmlDocCopyNode(subtree, fake, 1);
6967 fake->URL = xmlNodeGetBase(doc, subtree->parent);
6968 #ifdef WITH_XSLT_DEBUG
6969 xsltGenericDebug(xsltGenericDebugContext,
6970 "set base URI for embedded stylesheet as %s\n",
6971 fake->URL);
6972 #endif
6975 * Add all namespaces in scope of embedded stylesheet to
6976 * root element of newly created stylesheet document
6978 while ((subtree = subtree->parent) != (xmlNodePtr)doc) {
6979 for (ns = subtree->ns; ns; ns = ns->next) {
6980 xmlNewNs(newtree, ns->href, ns->prefix);
6984 xmlAddChild((xmlNodePtr)fake, newtree);
6985 ret = xsltParseStylesheetDoc(fake);
6986 if (ret == NULL)
6987 xmlFreeDoc(fake);
6990 } else {
6991 xmlChar *URL, *base;
6994 * Reference to an external stylesheet
6997 base = xmlNodeGetBase(doc, (xmlNodePtr) doc);
6998 URL = xmlBuildURI(href, base);
6999 if (URL != NULL) {
7000 #ifdef WITH_XSLT_DEBUG_PARSING
7001 xsltGenericDebug(xsltGenericDebugContext,
7002 "xsltLoadStylesheetPI : fetching %s\n", URL);
7003 #endif
7004 ret = xsltParseStylesheetFile(URL);
7005 xmlFree(URL);
7006 } else {
7007 #ifdef WITH_XSLT_DEBUG_PARSING
7008 xsltGenericDebug(xsltGenericDebugContext,
7009 "xsltLoadStylesheetPI : fetching %s\n", href);
7010 #endif
7011 ret = xsltParseStylesheetFile(href);
7013 if (base != NULL)
7014 xmlFree(base);
7016 xmlFreeURI(URI);
7017 xmlFree(href);
7019 return(ret);