xslt: Import upstream release 1.1.35.
[wine.git] / libs / xslt / libxslt / xslt.c
blob69116f2be2e56ce1756b5649e19b0697ec2393e8
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
95 * Harmless but avoiding a problem when compiling against a
96 * libxml <= 2.3.11 without LIBXML_DEBUG_ENABLED
98 #ifndef LIBXML_DEBUG_ENABLED
99 double xmlXPathStringEvalNumber(const xmlChar *str);
100 #endif
102 * Useful macros
105 #ifdef IS_BLANK
106 #undef IS_BLANK
107 #endif
108 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
109 ((c) == 0x0D))
111 #ifdef IS_BLANK_NODE
112 #undef IS_BLANK_NODE
113 #endif
114 #define IS_BLANK_NODE(n) \
115 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
118 * xsltParseContentError:
120 * @style: the stylesheet
121 * @node: the node where the error occured
123 * Compile-time error function.
125 static void
126 xsltParseContentError(xsltStylesheetPtr style,
127 xmlNodePtr node)
129 if ((style == NULL) || (node == NULL))
130 return;
132 if (IS_XSLT_ELEM(node))
133 xsltTransformError(NULL, style, node,
134 "The XSLT-element '%s' is not allowed at this position.\n",
135 node->name);
136 else
137 xsltTransformError(NULL, style, node,
138 "The element '%s' is not allowed at this position.\n",
139 node->name);
140 style->errors++;
143 #ifdef XSLT_REFACTORED
144 #else
146 * exclPrefixPush:
147 * @style: the transformation stylesheet
148 * @value: the excluded namespace name to push on the stack
150 * Push an excluded namespace name on the stack
152 * Returns the new index in the stack or -1 if already present or
153 * in case of error
155 static int
156 exclPrefixPush(xsltStylesheetPtr style, xmlChar * value)
158 int i;
160 if (style->exclPrefixMax == 0) {
161 style->exclPrefixMax = 4;
162 style->exclPrefixTab =
163 (xmlChar * *)xmlMalloc(style->exclPrefixMax *
164 sizeof(style->exclPrefixTab[0]));
165 if (style->exclPrefixTab == NULL) {
166 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
167 return (-1);
170 /* do not push duplicates */
171 for (i = 0;i < style->exclPrefixNr;i++) {
172 if (xmlStrEqual(style->exclPrefixTab[i], value))
173 return(-1);
175 if (style->exclPrefixNr >= style->exclPrefixMax) {
176 style->exclPrefixMax *= 2;
177 style->exclPrefixTab =
178 (xmlChar * *)xmlRealloc(style->exclPrefixTab,
179 style->exclPrefixMax *
180 sizeof(style->exclPrefixTab[0]));
181 if (style->exclPrefixTab == NULL) {
182 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
183 return (-1);
186 style->exclPrefixTab[style->exclPrefixNr] = value;
187 style->exclPrefix = value;
188 return (style->exclPrefixNr++);
191 * exclPrefixPop:
192 * @style: the transformation stylesheet
194 * Pop an excluded prefix value from the stack
196 * Returns the stored excluded prefix value
198 static xmlChar *
199 exclPrefixPop(xsltStylesheetPtr style)
201 xmlChar *ret;
203 if (style->exclPrefixNr <= 0)
204 return (0);
205 style->exclPrefixNr--;
206 if (style->exclPrefixNr > 0)
207 style->exclPrefix = style->exclPrefixTab[style->exclPrefixNr - 1];
208 else
209 style->exclPrefix = NULL;
210 ret = style->exclPrefixTab[style->exclPrefixNr];
211 style->exclPrefixTab[style->exclPrefixNr] = 0;
212 return (ret);
214 #endif
216 /************************************************************************
218 * Helper functions *
220 ************************************************************************/
222 static int initialized = 0;
224 * xsltInit:
226 * Initializes the processor (e.g. registers built-in extensions,
227 * etc.)
229 void
230 xsltInit (void) {
231 if (initialized == 0) {
232 initialized = 1;
233 #ifdef XSLT_LOCALE_WINAPI
234 xsltLocaleMutex = xmlNewRMutex();
235 #endif
236 xsltRegisterAllExtras();
241 * xsltUninit:
243 * Uninitializes the processor.
245 void
246 xsltUninit (void) {
247 #ifdef XSLT_LOCALE_WINAPI
248 xmlFreeRMutex(xsltLocaleMutex);
249 xsltLocaleMutex = NULL;
250 #endif
251 initialized = 0;
255 * xsltIsBlank:
256 * @str: a string
258 * Check if a string is ignorable
260 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
263 xsltIsBlank(xmlChar *str) {
264 if (str == NULL)
265 return(1);
266 while (*str != 0) {
267 if (!(IS_BLANK(*str))) return(0);
268 str++;
270 return(1);
273 /************************************************************************
275 * Routines to handle XSLT data structures *
277 ************************************************************************/
278 static xsltDecimalFormatPtr
279 xsltNewDecimalFormat(const xmlChar *nsUri, xmlChar *name)
281 xsltDecimalFormatPtr self;
282 /* UTF-8 for 0x2030 */
283 static const xmlChar permille[4] = {0xe2, 0x80, 0xb0, 0};
285 self = xmlMalloc(sizeof(xsltDecimalFormat));
286 if (self != NULL) {
287 self->next = NULL;
288 self->nsUri = nsUri;
289 self->name = name;
291 /* Default values */
292 self->digit = xmlStrdup(BAD_CAST("#"));
293 self->patternSeparator = xmlStrdup(BAD_CAST(";"));
294 self->decimalPoint = xmlStrdup(BAD_CAST("."));
295 self->grouping = xmlStrdup(BAD_CAST(","));
296 self->percent = xmlStrdup(BAD_CAST("%"));
297 self->permille = xmlStrdup(BAD_CAST(permille));
298 self->zeroDigit = xmlStrdup(BAD_CAST("0"));
299 self->minusSign = xmlStrdup(BAD_CAST("-"));
300 self->infinity = xmlStrdup(BAD_CAST("Infinity"));
301 self->noNumber = xmlStrdup(BAD_CAST("NaN"));
303 return self;
306 static void
307 xsltFreeDecimalFormat(xsltDecimalFormatPtr self)
309 if (self != NULL) {
310 if (self->digit)
311 xmlFree(self->digit);
312 if (self->patternSeparator)
313 xmlFree(self->patternSeparator);
314 if (self->decimalPoint)
315 xmlFree(self->decimalPoint);
316 if (self->grouping)
317 xmlFree(self->grouping);
318 if (self->percent)
319 xmlFree(self->percent);
320 if (self->permille)
321 xmlFree(self->permille);
322 if (self->zeroDigit)
323 xmlFree(self->zeroDigit);
324 if (self->minusSign)
325 xmlFree(self->minusSign);
326 if (self->infinity)
327 xmlFree(self->infinity);
328 if (self->noNumber)
329 xmlFree(self->noNumber);
330 if (self->name)
331 xmlFree(self->name);
332 xmlFree(self);
336 static void
337 xsltFreeDecimalFormatList(xsltStylesheetPtr self)
339 xsltDecimalFormatPtr iter;
340 xsltDecimalFormatPtr tmp;
342 if (self == NULL)
343 return;
345 iter = self->decimalFormat;
346 while (iter != NULL) {
347 tmp = iter->next;
348 xsltFreeDecimalFormat(iter);
349 iter = tmp;
354 * xsltDecimalFormatGetByName:
355 * @style: the XSLT stylesheet
356 * @name: the decimal-format name to find
358 * Find decimal-format by name
360 * Returns the xsltDecimalFormatPtr
362 xsltDecimalFormatPtr
363 xsltDecimalFormatGetByName(xsltStylesheetPtr style, xmlChar *name)
365 xsltDecimalFormatPtr result = NULL;
367 if (name == NULL)
368 return style->decimalFormat;
370 while (style != NULL) {
371 for (result = style->decimalFormat->next;
372 result != NULL;
373 result = result->next) {
374 if ((result->nsUri == NULL) && xmlStrEqual(name, result->name))
375 return result;
377 style = xsltNextImport(style);
379 return result;
383 * xsltDecimalFormatGetByQName:
384 * @style: the XSLT stylesheet
385 * @nsUri: the namespace URI of the QName
386 * @name: the local part of the QName
388 * Find decimal-format by QName
390 * Returns the xsltDecimalFormatPtr
392 xsltDecimalFormatPtr
393 xsltDecimalFormatGetByQName(xsltStylesheetPtr style, const xmlChar *nsUri,
394 const xmlChar *name)
396 xsltDecimalFormatPtr result = NULL;
398 if (name == NULL)
399 return style->decimalFormat;
401 while (style != NULL) {
402 for (result = style->decimalFormat->next;
403 result != NULL;
404 result = result->next) {
405 if (xmlStrEqual(nsUri, result->nsUri) &&
406 xmlStrEqual(name, result->name))
407 return result;
409 style = xsltNextImport(style);
411 return result;
416 * xsltNewTemplate:
418 * Create a new XSLT Template
420 * Returns the newly allocated xsltTemplatePtr or NULL in case of error
422 static xsltTemplatePtr
423 xsltNewTemplate(void) {
424 xsltTemplatePtr cur;
426 cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate));
427 if (cur == NULL) {
428 xsltTransformError(NULL, NULL, NULL,
429 "xsltNewTemplate : malloc failed\n");
430 return(NULL);
432 memset(cur, 0, sizeof(xsltTemplate));
433 cur->priority = XSLT_PAT_NO_PRIORITY;
434 return(cur);
438 * xsltFreeTemplate:
439 * @template: an XSLT template
441 * Free up the memory allocated by @template
443 static void
444 xsltFreeTemplate(xsltTemplatePtr template) {
445 if (template == NULL)
446 return;
447 if (template->match) xmlFree(template->match);
449 * NOTE: @name and @nameURI are put into the string dict now.
450 * if (template->name) xmlFree(template->name);
451 * if (template->nameURI) xmlFree(template->nameURI);
454 if (template->mode) xmlFree(template->mode);
455 if (template->modeURI) xmlFree(template->modeURI);
457 if (template->inheritedNs) xmlFree(template->inheritedNs);
459 /* free profiling data */
460 if (template->templCalledTab) xmlFree(template->templCalledTab);
461 if (template->templCountTab) xmlFree(template->templCountTab);
463 memset(template, -1, sizeof(xsltTemplate));
464 xmlFree(template);
468 * xsltFreeTemplateList:
469 * @template: an XSLT template list
471 * Free up the memory allocated by all the elements of @template
473 static void
474 xsltFreeTemplateList(xsltTemplatePtr template) {
475 xsltTemplatePtr cur;
477 while (template != NULL) {
478 cur = template;
479 template = template->next;
480 xsltFreeTemplate(cur);
484 #ifdef XSLT_REFACTORED
486 static void
487 xsltFreeNsAliasList(xsltNsAliasPtr item)
489 xsltNsAliasPtr tmp;
491 while (item) {
492 tmp = item;
493 item = item->next;
494 xmlFree(tmp);
496 return;
499 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
500 static void
501 xsltFreeNamespaceMap(xsltNsMapPtr item)
503 xsltNsMapPtr tmp;
505 while (item) {
506 tmp = item;
507 item = item->next;
508 xmlFree(tmp);
510 return;
513 static xsltNsMapPtr
514 xsltNewNamespaceMapItem(xsltCompilerCtxtPtr cctxt,
515 xmlDocPtr doc,
516 xmlNsPtr ns,
517 xmlNodePtr elem)
519 xsltNsMapPtr ret;
521 if ((cctxt == NULL) || (doc == NULL) || (ns == NULL))
522 return(NULL);
524 ret = (xsltNsMapPtr) xmlMalloc(sizeof(xsltNsMap));
525 if (ret == NULL) {
526 xsltTransformError(NULL, cctxt->style, elem,
527 "Internal error: (xsltNewNamespaceMapItem) "
528 "memory allocation failed.\n");
529 return(NULL);
531 memset(ret, 0, sizeof(xsltNsMap));
532 ret->doc = doc;
533 ret->ns = ns;
534 ret->origNsName = ns->href;
536 * Store the item at current stylesheet-level.
538 if (cctxt->psData->nsMap != NULL)
539 ret->next = cctxt->psData->nsMap;
540 cctxt->psData->nsMap = ret;
542 return(ret);
544 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
547 * xsltCompilerVarInfoFree:
548 * @cctxt: the compilation context
550 * Frees the list of information for vars/params.
552 static void
553 xsltCompilerVarInfoFree(xsltCompilerCtxtPtr cctxt)
555 xsltVarInfoPtr ivar = cctxt->ivars, ivartmp;
557 while (ivar) {
558 ivartmp = ivar;
559 ivar = ivar->next;
560 xmlFree(ivartmp);
565 * xsltCompilerCtxtFree:
567 * Free an XSLT compiler context.
569 static void
570 xsltCompilationCtxtFree(xsltCompilerCtxtPtr cctxt)
572 if (cctxt == NULL)
573 return;
574 #ifdef WITH_XSLT_DEBUG_PARSING
575 xsltGenericDebug(xsltGenericDebugContext,
576 "Freeing compilation context\n");
577 xsltGenericDebug(xsltGenericDebugContext,
578 "### Max inodes: %d\n", cctxt->maxNodeInfos);
579 xsltGenericDebug(xsltGenericDebugContext,
580 "### Max LREs : %d\n", cctxt->maxLREs);
581 #endif
583 * Free node-infos.
585 if (cctxt->inodeList != NULL) {
586 xsltCompilerNodeInfoPtr tmp, cur = cctxt->inodeList;
587 while (cur != NULL) {
588 tmp = cur;
589 cur = cur->next;
590 xmlFree(tmp);
593 if (cctxt->tmpList != NULL)
594 xsltPointerListFree(cctxt->tmpList);
595 if (cctxt->nsAliases != NULL)
596 xsltFreeNsAliasList(cctxt->nsAliases);
598 if (cctxt->ivars)
599 xsltCompilerVarInfoFree(cctxt);
601 xmlFree(cctxt);
605 * xsltCompilerCreate:
607 * Creates an XSLT compiler context.
609 * Returns the pointer to the created xsltCompilerCtxt or
610 * NULL in case of an internal error.
612 static xsltCompilerCtxtPtr
613 xsltCompilationCtxtCreate(xsltStylesheetPtr style) {
614 xsltCompilerCtxtPtr ret;
616 ret = (xsltCompilerCtxtPtr) xmlMalloc(sizeof(xsltCompilerCtxt));
617 if (ret == NULL) {
618 xsltTransformError(NULL, style, NULL,
619 "xsltCompilerCreate: allocation of compiler "
620 "context failed.\n");
621 return(NULL);
623 memset(ret, 0, sizeof(xsltCompilerCtxt));
625 ret->errSeverity = XSLT_ERROR_SEVERITY_ERROR;
626 ret->tmpList = xsltPointerListCreate(20);
627 if (ret->tmpList == NULL) {
628 goto internal_err;
631 return(ret);
633 internal_err:
634 xsltCompilationCtxtFree(ret);
635 return(NULL);
638 static void
639 xsltLREEffectiveNsNodesFree(xsltEffectiveNsPtr first)
641 xsltEffectiveNsPtr tmp;
643 while (first != NULL) {
644 tmp = first;
645 first = first->nextInStore;
646 xmlFree(tmp);
650 static void
651 xsltFreePrincipalStylesheetData(xsltPrincipalStylesheetDataPtr data)
653 if (data == NULL)
654 return;
656 if (data->inScopeNamespaces != NULL) {
657 int i;
658 xsltNsListContainerPtr nsi;
659 xsltPointerListPtr list =
660 (xsltPointerListPtr) data->inScopeNamespaces;
662 for (i = 0; i < list->number; i++) {
664 * REVISIT TODO: Free info of in-scope namespaces.
666 nsi = (xsltNsListContainerPtr) list->items[i];
667 if (nsi->list != NULL)
668 xmlFree(nsi->list);
669 xmlFree(nsi);
671 xsltPointerListFree(list);
672 data->inScopeNamespaces = NULL;
675 if (data->exclResultNamespaces != NULL) {
676 int i;
677 xsltPointerListPtr list = (xsltPointerListPtr)
678 data->exclResultNamespaces;
680 for (i = 0; i < list->number; i++)
681 xsltPointerListFree((xsltPointerListPtr) list->items[i]);
683 xsltPointerListFree(list);
684 data->exclResultNamespaces = NULL;
687 if (data->extElemNamespaces != NULL) {
688 xsltPointerListPtr list = (xsltPointerListPtr)
689 data->extElemNamespaces;
690 int i;
692 for (i = 0; i < list->number; i++)
693 xsltPointerListFree((xsltPointerListPtr) list->items[i]);
695 xsltPointerListFree(list);
696 data->extElemNamespaces = NULL;
698 if (data->effectiveNs) {
699 xsltLREEffectiveNsNodesFree(data->effectiveNs);
700 data->effectiveNs = NULL;
702 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
703 xsltFreeNamespaceMap(data->nsMap);
704 #endif
705 xmlFree(data);
708 static xsltPrincipalStylesheetDataPtr
709 xsltNewPrincipalStylesheetData(void)
711 xsltPrincipalStylesheetDataPtr ret;
713 ret = (xsltPrincipalStylesheetDataPtr)
714 xmlMalloc(sizeof(xsltPrincipalStylesheetData));
715 if (ret == NULL) {
716 xsltTransformError(NULL, NULL, NULL,
717 "xsltNewPrincipalStylesheetData: memory allocation failed.\n");
718 return(NULL);
720 memset(ret, 0, sizeof(xsltPrincipalStylesheetData));
723 * Global list of in-scope namespaces.
725 ret->inScopeNamespaces = xsltPointerListCreate(-1);
726 if (ret->inScopeNamespaces == NULL)
727 goto internal_err;
729 * Global list of excluded result ns-decls.
731 ret->exclResultNamespaces = xsltPointerListCreate(-1);
732 if (ret->exclResultNamespaces == NULL)
733 goto internal_err;
735 * Global list of extension instruction namespace names.
737 ret->extElemNamespaces = xsltPointerListCreate(-1);
738 if (ret->extElemNamespaces == NULL)
739 goto internal_err;
741 return(ret);
743 internal_err:
745 return(NULL);
748 #endif
751 * xsltNewStylesheetInternal:
752 * @parent: the parent stylesheet or NULL
754 * Create a new XSLT Stylesheet
756 * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
758 static xsltStylesheetPtr
759 xsltNewStylesheetInternal(xsltStylesheetPtr parent) {
760 xsltStylesheetPtr ret = NULL;
762 ret = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet));
763 if (ret == NULL) {
764 xsltTransformError(NULL, NULL, NULL,
765 "xsltNewStylesheet : malloc failed\n");
766 goto internal_err;
768 memset(ret, 0, sizeof(xsltStylesheet));
770 ret->parent = parent;
771 ret->omitXmlDeclaration = -1;
772 ret->standalone = -1;
773 ret->decimalFormat = xsltNewDecimalFormat(NULL, NULL);
774 ret->indent = -1;
775 ret->errors = 0;
776 ret->warnings = 0;
777 ret->exclPrefixNr = 0;
778 ret->exclPrefixMax = 0;
779 ret->exclPrefixTab = NULL;
780 ret->extInfos = NULL;
781 ret->extrasNr = 0;
782 ret->internalized = 1;
783 ret->literal_result = 0;
784 ret->forwards_compatible = 0;
785 ret->dict = xmlDictCreate();
786 #ifdef WITH_XSLT_DEBUG
787 xsltGenericDebug(xsltGenericDebugContext,
788 "creating dictionary for stylesheet\n");
789 #endif
791 if (parent == NULL) {
792 ret->principal = ret;
794 ret->xpathCtxt = xmlXPathNewContext(NULL);
795 if (ret->xpathCtxt == NULL) {
796 xsltTransformError(NULL, NULL, NULL,
797 "xsltNewStylesheet: xmlXPathNewContext failed\n");
798 goto internal_err;
800 if (xmlXPathContextSetCache(ret->xpathCtxt, 1, -1, 0) == -1)
801 goto internal_err;
802 } else {
803 ret->principal = parent->principal;
806 xsltInit();
808 return(ret);
810 internal_err:
811 if (ret != NULL)
812 xsltFreeStylesheet(ret);
813 return(NULL);
817 * xsltNewStylesheet:
819 * Create a new XSLT Stylesheet
821 * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
823 xsltStylesheetPtr
824 xsltNewStylesheet(void) {
825 return xsltNewStylesheetInternal(NULL);
829 * xsltAllocateExtra:
830 * @style: an XSLT stylesheet
832 * Allocate an extra runtime information slot statically while compiling
833 * the stylesheet and return its number
835 * Returns the number of the slot
838 xsltAllocateExtra(xsltStylesheetPtr style)
840 return(style->extrasNr++);
844 * xsltAllocateExtraCtxt:
845 * @ctxt: an XSLT transformation context
847 * Allocate an extra runtime information slot at run-time
848 * and return its number
849 * This make sure there is a slot ready in the transformation context
851 * Returns the number of the slot
854 xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt)
856 if (ctxt->extrasNr >= ctxt->extrasMax) {
857 int i;
858 if (ctxt->extrasNr == 0) {
859 ctxt->extrasMax = 20;
860 ctxt->extras = (xsltRuntimeExtraPtr)
861 xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra));
862 if (ctxt->extras == NULL) {
863 xsltTransformError(ctxt, NULL, NULL,
864 "xsltAllocateExtraCtxt: out of memory\n");
865 return(0);
867 for (i = 0;i < ctxt->extrasMax;i++) {
868 ctxt->extras[i].info = NULL;
869 ctxt->extras[i].deallocate = NULL;
870 ctxt->extras[i].val.ptr = NULL;
873 } else {
874 xsltRuntimeExtraPtr tmp;
876 ctxt->extrasMax += 100;
877 tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras,
878 ctxt->extrasMax * sizeof(xsltRuntimeExtra));
879 if (tmp == NULL) {
880 xsltTransformError(ctxt, NULL, NULL,
881 "xsltAllocateExtraCtxt: out of memory\n");
882 return(0);
884 ctxt->extras = tmp;
885 for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) {
886 ctxt->extras[i].info = NULL;
887 ctxt->extras[i].deallocate = NULL;
888 ctxt->extras[i].val.ptr = NULL;
892 return(ctxt->extrasNr++);
896 * xsltFreeStylesheetList:
897 * @style: an XSLT stylesheet list
899 * Free up the memory allocated by the list @style
901 static void
902 xsltFreeStylesheetList(xsltStylesheetPtr style) {
903 xsltStylesheetPtr next;
905 while (style != NULL) {
906 next = style->next;
907 xsltFreeStylesheet(style);
908 style = next;
913 * xsltCleanupStylesheetTree:
915 * @doc: the document-node
916 * @node: the element where the stylesheet is rooted at
918 * Actually @node need not be the document-element, but
919 * currently Libxslt does not support embedded stylesheets.
921 * Returns 0 if OK, -1 on API or internal errors.
923 static int
924 xsltCleanupStylesheetTree(xmlDocPtr doc ATTRIBUTE_UNUSED,
925 xmlNodePtr rootElem ATTRIBUTE_UNUSED)
927 #if 0 /* TODO: Currently disabled, since probably not needed. */
928 xmlNodePtr cur;
930 if ((doc == NULL) || (rootElem == NULL) ||
931 (rootElem->type != XML_ELEMENT_NODE) ||
932 (doc != rootElem->doc))
933 return(-1);
936 * Cleanup was suggested by Aleksey Sanin:
937 * Clear the PSVI field to avoid problems if the
938 * node-tree of the stylesheet is intended to be used for
939 * further processing by the user (e.g. for compiling it
940 * once again - although not recommended).
943 cur = rootElem;
944 while (cur != NULL) {
945 if (cur->type == XML_ELEMENT_NODE) {
947 * Clear the PSVI field.
949 cur->psvi = NULL;
950 if (cur->children) {
951 cur = cur->children;
952 continue;
956 leave_node:
957 if (cur == rootElem)
958 break;
959 if (cur->next != NULL)
960 cur = cur->next;
961 else {
962 cur = cur->parent;
963 if (cur == NULL)
964 break;
965 goto leave_node;
968 #endif /* #if 0 */
969 return(0);
973 * xsltFreeStylesheet:
974 * @style: an XSLT stylesheet
976 * Free up the memory allocated by @style
978 void
979 xsltFreeStylesheet(xsltStylesheetPtr style)
981 if (style == NULL)
982 return;
984 #ifdef XSLT_REFACTORED
986 * Start with a cleanup of the main stylesheet's doc.
988 if ((style->principal == style) && (style->doc))
989 xsltCleanupStylesheetTree(style->doc,
990 xmlDocGetRootElement(style->doc));
991 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
993 * Restore changed ns-decls before freeing the document.
995 if ((style->doc != NULL) &&
996 XSLT_HAS_INTERNAL_NSMAP(style))
998 xsltRestoreDocumentNamespaces(XSLT_GET_INTERNAL_NSMAP(style),
999 style->doc);
1001 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
1002 #else
1004 * Start with a cleanup of the main stylesheet's doc.
1006 if ((style->parent == NULL) && (style->doc))
1007 xsltCleanupStylesheetTree(style->doc,
1008 xmlDocGetRootElement(style->doc));
1009 #endif /* XSLT_REFACTORED */
1011 xsltFreeKeys(style);
1012 xsltFreeExts(style);
1013 xsltFreeTemplateHashes(style);
1014 xsltFreeDecimalFormatList(style);
1015 xsltFreeTemplateList(style->templates);
1016 xsltFreeAttributeSetsHashes(style);
1017 xsltFreeNamespaceAliasHashes(style);
1018 xsltFreeStylePreComps(style);
1020 * Free documents of all included stylsheet modules of this
1021 * stylesheet level.
1023 xsltFreeStyleDocuments(style);
1025 * TODO: Best time to shutdown extension stuff?
1027 xsltShutdownExts(style);
1029 if (style->variables != NULL)
1030 xsltFreeStackElemList(style->variables);
1031 if (style->cdataSection != NULL)
1032 xmlHashFree(style->cdataSection, NULL);
1033 if (style->stripSpaces != NULL)
1034 xmlHashFree(style->stripSpaces, NULL);
1035 if (style->nsHash != NULL)
1036 xmlHashFree(style->nsHash, NULL);
1037 if (style->exclPrefixTab != NULL)
1038 xmlFree(style->exclPrefixTab);
1039 if (style->method != NULL)
1040 xmlFree(style->method);
1041 if (style->methodURI != NULL)
1042 xmlFree(style->methodURI);
1043 if (style->version != NULL)
1044 xmlFree(style->version);
1045 if (style->encoding != NULL)
1046 xmlFree(style->encoding);
1047 if (style->doctypePublic != NULL)
1048 xmlFree(style->doctypePublic);
1049 if (style->doctypeSystem != NULL)
1050 xmlFree(style->doctypeSystem);
1051 if (style->mediaType != NULL)
1052 xmlFree(style->mediaType);
1053 if (style->attVTs)
1054 xsltFreeAVTList(style->attVTs);
1055 if (style->imports != NULL)
1056 xsltFreeStylesheetList(style->imports);
1058 #ifdef XSLT_REFACTORED
1060 * If this is the principal stylesheet, then
1061 * free its internal data.
1063 if (style->principal == style) {
1064 if (style->principalData) {
1065 xsltFreePrincipalStylesheetData(style->principalData);
1066 style->principalData = NULL;
1069 #endif
1071 * Better to free the main document of this stylesheet level
1072 * at the end - so here.
1074 if (style->doc != NULL) {
1075 xmlFreeDoc(style->doc);
1078 #ifdef WITH_XSLT_DEBUG
1079 xsltGenericDebug(xsltGenericDebugContext,
1080 "freeing dictionary from stylesheet\n");
1081 #endif
1082 xmlDictFree(style->dict);
1084 if (style->xpathCtxt != NULL)
1085 xmlXPathFreeContext(style->xpathCtxt);
1087 memset(style, -1, sizeof(xsltStylesheet));
1088 xmlFree(style);
1091 /************************************************************************
1093 * Parsing of an XSLT Stylesheet *
1095 ************************************************************************/
1097 #ifdef XSLT_REFACTORED
1099 * This is now performed in an optimized way in xsltParseXSLTTemplate.
1101 #else
1103 * xsltGetInheritedNsList:
1104 * @style: the stylesheet
1105 * @template: the template
1106 * @node: the current node
1108 * Search all the namespace applying to a given element except the ones
1109 * from excluded output prefixes currently in scope. Initialize the
1110 * template inheritedNs list with it.
1112 * Returns the number of entries found
1114 static int
1115 xsltGetInheritedNsList(xsltStylesheetPtr style,
1116 xsltTemplatePtr template,
1117 xmlNodePtr node)
1119 xmlNsPtr cur;
1120 xmlNsPtr *ret = NULL;
1121 int nbns = 0;
1122 int maxns = 10;
1123 int i;
1125 if ((style == NULL) || (template == NULL) || (node == NULL) ||
1126 (template->inheritedNsNr != 0) || (template->inheritedNs != NULL))
1127 return(0);
1128 while (node != NULL) {
1129 if (node->type == XML_ELEMENT_NODE) {
1130 cur = node->nsDef;
1131 while (cur != NULL) {
1132 if (xmlStrEqual(cur->href, XSLT_NAMESPACE))
1133 goto skip_ns;
1135 if ((cur->prefix != NULL) &&
1136 (xsltCheckExtPrefix(style, cur->prefix)))
1137 goto skip_ns;
1139 * Check if this namespace was excluded.
1140 * Note that at this point only the exclusions defined
1141 * on the topmost stylesheet element are in the exclusion-list.
1143 for (i = 0;i < style->exclPrefixNr;i++) {
1144 if (xmlStrEqual(cur->href, style->exclPrefixTab[i]))
1145 goto skip_ns;
1147 if (ret == NULL) {
1148 ret =
1149 (xmlNsPtr *) xmlMalloc((maxns + 1) *
1150 sizeof(xmlNsPtr));
1151 if (ret == NULL) {
1152 xmlGenericError(xmlGenericErrorContext,
1153 "xsltGetInheritedNsList : out of memory!\n");
1154 return(0);
1156 ret[nbns] = NULL;
1159 * Skip shadowed namespace bindings.
1161 for (i = 0; i < nbns; i++) {
1162 if ((cur->prefix == ret[i]->prefix) ||
1163 (xmlStrEqual(cur->prefix, ret[i]->prefix)))
1164 break;
1166 if (i >= nbns) {
1167 if (nbns >= maxns) {
1168 maxns *= 2;
1169 ret = (xmlNsPtr *) xmlRealloc(ret,
1170 (maxns +
1171 1) *
1172 sizeof(xmlNsPtr));
1173 if (ret == NULL) {
1174 xmlGenericError(xmlGenericErrorContext,
1175 "xsltGetInheritedNsList : realloc failed!\n");
1176 return(0);
1179 ret[nbns++] = cur;
1180 ret[nbns] = NULL;
1182 skip_ns:
1183 cur = cur->next;
1186 node = node->parent;
1188 if (nbns != 0) {
1189 #ifdef WITH_XSLT_DEBUG_PARSING
1190 xsltGenericDebug(xsltGenericDebugContext,
1191 "template has %d inherited namespaces\n", nbns);
1192 #endif
1193 template->inheritedNsNr = nbns;
1194 template->inheritedNs = ret;
1196 return (nbns);
1198 #endif /* else of XSLT_REFACTORED */
1201 * xsltParseStylesheetOutput:
1202 * @style: the XSLT stylesheet
1203 * @cur: the "output" element
1205 * parse an XSLT stylesheet output element and record
1206 * information related to the stylesheet output
1209 void
1210 xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur)
1212 xmlChar *elements,
1213 *prop;
1214 xmlChar *element,
1215 *end;
1217 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1218 return;
1220 prop = xmlGetNsProp(cur, (const xmlChar *) "version", NULL);
1221 if (prop != NULL) {
1222 if (style->version != NULL)
1223 xmlFree(style->version);
1224 style->version = prop;
1227 prop = xmlGetNsProp(cur, (const xmlChar *) "encoding", NULL);
1228 if (prop != NULL) {
1229 if (style->encoding != NULL)
1230 xmlFree(style->encoding);
1231 style->encoding = prop;
1234 /* relaxed to support xt:document
1235 * TODO KB: What does "relaxed to support xt:document" mean?
1237 prop = xmlGetNsProp(cur, (const xmlChar *) "method", NULL);
1238 if (prop != NULL) {
1239 const xmlChar *URI;
1241 if (style->method != NULL)
1242 xmlFree(style->method);
1243 style->method = NULL;
1244 if (style->methodURI != NULL)
1245 xmlFree(style->methodURI);
1246 style->methodURI = NULL;
1249 * TODO: Don't use xsltGetQNameURI().
1251 URI = xsltGetQNameURI(cur, &prop);
1252 if (prop == NULL) {
1253 if (style != NULL) style->errors++;
1254 } else if (URI == NULL) {
1255 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
1256 (xmlStrEqual(prop, (const xmlChar *) "html")) ||
1257 (xmlStrEqual(prop, (const xmlChar *) "text"))) {
1258 style->method = prop;
1259 } else {
1260 xsltTransformError(NULL, style, cur,
1261 "invalid value for method: %s\n", prop);
1262 if (style != NULL) style->warnings++;
1263 xmlFree(prop);
1265 } else {
1266 style->method = prop;
1267 style->methodURI = xmlStrdup(URI);
1271 prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-system", NULL);
1272 if (prop != NULL) {
1273 if (style->doctypeSystem != NULL)
1274 xmlFree(style->doctypeSystem);
1275 style->doctypeSystem = prop;
1278 prop = xmlGetNsProp(cur, (const xmlChar *) "doctype-public", NULL);
1279 if (prop != NULL) {
1280 if (style->doctypePublic != NULL)
1281 xmlFree(style->doctypePublic);
1282 style->doctypePublic = prop;
1285 prop = xmlGetNsProp(cur, (const xmlChar *) "standalone", NULL);
1286 if (prop != NULL) {
1287 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1288 style->standalone = 1;
1289 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1290 style->standalone = 0;
1291 } else {
1292 xsltTransformError(NULL, style, cur,
1293 "invalid value for standalone: %s\n", prop);
1294 style->errors++;
1296 xmlFree(prop);
1299 prop = xmlGetNsProp(cur, (const xmlChar *) "indent", NULL);
1300 if (prop != NULL) {
1301 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1302 style->indent = 1;
1303 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1304 style->indent = 0;
1305 } else {
1306 xsltTransformError(NULL, style, cur,
1307 "invalid value for indent: %s\n", prop);
1308 style->errors++;
1310 xmlFree(prop);
1313 prop = xmlGetNsProp(cur, (const xmlChar *) "omit-xml-declaration", NULL);
1314 if (prop != NULL) {
1315 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1316 style->omitXmlDeclaration = 1;
1317 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1318 style->omitXmlDeclaration = 0;
1319 } else {
1320 xsltTransformError(NULL, style, cur,
1321 "invalid value for omit-xml-declaration: %s\n",
1322 prop);
1323 style->errors++;
1325 xmlFree(prop);
1328 elements = xmlGetNsProp(cur, (const xmlChar *) "cdata-section-elements",
1329 NULL);
1330 if (elements != NULL) {
1331 if (style->cdataSection == NULL)
1332 style->cdataSection = xmlHashCreate(10);
1333 if (style->cdataSection == NULL)
1334 return;
1336 element = elements;
1337 while (*element != 0) {
1338 while (IS_BLANK(*element))
1339 element++;
1340 if (*element == 0)
1341 break;
1342 end = element;
1343 while ((*end != 0) && (!IS_BLANK(*end)))
1344 end++;
1345 element = xmlStrndup(element, end - element);
1346 if (element) {
1347 #ifdef WITH_XSLT_DEBUG_PARSING
1348 xsltGenericDebug(xsltGenericDebugContext,
1349 "add cdata section output element %s\n",
1350 element);
1351 #endif
1352 if (xmlValidateQName(BAD_CAST element, 0) != 0) {
1353 xsltTransformError(NULL, style, cur,
1354 "Attribute 'cdata-section-elements': The value "
1355 "'%s' is not a valid QName.\n", element);
1356 xmlFree(element);
1357 style->errors++;
1358 } else {
1359 const xmlChar *URI;
1362 * TODO: Don't use xsltGetQNameURI().
1364 URI = xsltGetQNameURI(cur, &element);
1365 if (element == NULL) {
1367 * TODO: We'll report additionally an error
1368 * via the stylesheet's error handling.
1370 xsltTransformError(NULL, style, cur,
1371 "Attribute 'cdata-section-elements': "
1372 "Not a valid QName.\n");
1373 style->errors++;
1374 } else {
1375 xmlNsPtr ns;
1378 * XSLT-1.0 "Each QName is expanded into an
1379 * expanded-name using the namespace declarations in
1380 * effect on the xsl:output element in which the QName
1381 * occurs; if there is a default namespace, it is used
1382 * for QNames that do not have a prefix"
1383 * NOTE: Fix of bug #339570.
1385 if (URI == NULL) {
1386 ns = xmlSearchNs(style->doc, cur, NULL);
1387 if (ns != NULL)
1388 URI = ns->href;
1390 xmlHashAddEntry2(style->cdataSection, element, URI,
1391 (void *) "cdata");
1392 xmlFree(element);
1396 element = end;
1398 xmlFree(elements);
1401 prop = xmlGetNsProp(cur, (const xmlChar *) "media-type", NULL);
1402 if (prop != NULL) {
1403 if (style->mediaType)
1404 xmlFree(style->mediaType);
1405 style->mediaType = prop;
1407 if (cur->children != NULL) {
1408 xsltParseContentError(style, cur->children);
1413 * xsltParseStylesheetDecimalFormat:
1414 * @style: the XSLT stylesheet
1415 * @cur: the "decimal-format" element
1417 * <!-- Category: top-level-element -->
1418 * <xsl:decimal-format
1419 * name = qname, decimal-separator = char, grouping-separator = char,
1420 * infinity = string, minus-sign = char, NaN = string, percent = char
1421 * per-mille = char, zero-digit = char, digit = char,
1422 * pattern-separator = char />
1424 * parse an XSLT stylesheet decimal-format element and
1425 * and record the formatting characteristics
1427 static void
1428 xsltParseStylesheetDecimalFormat(xsltStylesheetPtr style, xmlNodePtr cur)
1430 xmlChar *prop;
1431 xsltDecimalFormatPtr format;
1432 xsltDecimalFormatPtr iter;
1434 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1435 return;
1437 format = style->decimalFormat;
1439 prop = xmlGetNsProp(cur, BAD_CAST("name"), NULL);
1440 if (prop != NULL) {
1441 const xmlChar *nsUri;
1443 if (xmlValidateQName(prop, 0) != 0) {
1444 xsltTransformError(NULL, style, cur,
1445 "xsl:decimal-format: Invalid QName '%s'.\n", prop);
1446 style->warnings++;
1447 xmlFree(prop);
1448 return;
1451 * TODO: Don't use xsltGetQNameURI().
1453 nsUri = xsltGetQNameURI(cur, &prop);
1454 if (prop == NULL) {
1455 style->warnings++;
1456 return;
1458 format = xsltDecimalFormatGetByQName(style, nsUri, prop);
1459 if (format != NULL) {
1460 xsltTransformError(NULL, style, cur,
1461 "xsltParseStylestyleDecimalFormat: %s already exists\n", prop);
1462 style->warnings++;
1463 xmlFree(prop);
1464 return;
1466 format = xsltNewDecimalFormat(nsUri, prop);
1467 if (format == NULL) {
1468 xsltTransformError(NULL, style, cur,
1469 "xsltParseStylestyleDecimalFormat: failed creating new decimal-format\n");
1470 style->errors++;
1471 xmlFree(prop);
1472 return;
1474 /* Append new decimal-format structure */
1475 for (iter = style->decimalFormat; iter->next; iter = iter->next)
1477 if (iter)
1478 iter->next = format;
1481 prop = xmlGetNsProp(cur, (const xmlChar *)"decimal-separator", NULL);
1482 if (prop != NULL) {
1483 if (format->decimalPoint != NULL) xmlFree(format->decimalPoint);
1484 format->decimalPoint = prop;
1487 prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", NULL);
1488 if (prop != NULL) {
1489 if (format->grouping != NULL) xmlFree(format->grouping);
1490 format->grouping = prop;
1493 prop = xmlGetNsProp(cur, (const xmlChar *)"infinity", NULL);
1494 if (prop != NULL) {
1495 if (format->infinity != NULL) xmlFree(format->infinity);
1496 format->infinity = prop;
1499 prop = xmlGetNsProp(cur, (const xmlChar *)"minus-sign", NULL);
1500 if (prop != NULL) {
1501 if (format->minusSign != NULL) xmlFree(format->minusSign);
1502 format->minusSign = prop;
1505 prop = xmlGetNsProp(cur, (const xmlChar *)"NaN", NULL);
1506 if (prop != NULL) {
1507 if (format->noNumber != NULL) xmlFree(format->noNumber);
1508 format->noNumber = prop;
1511 prop = xmlGetNsProp(cur, (const xmlChar *)"percent", NULL);
1512 if (prop != NULL) {
1513 if (format->percent != NULL) xmlFree(format->percent);
1514 format->percent = prop;
1517 prop = xmlGetNsProp(cur, (const xmlChar *)"per-mille", NULL);
1518 if (prop != NULL) {
1519 if (format->permille != NULL) xmlFree(format->permille);
1520 format->permille = prop;
1523 prop = xmlGetNsProp(cur, (const xmlChar *)"zero-digit", NULL);
1524 if (prop != NULL) {
1525 if (format->zeroDigit != NULL) xmlFree(format->zeroDigit);
1526 format->zeroDigit = prop;
1529 prop = xmlGetNsProp(cur, (const xmlChar *)"digit", NULL);
1530 if (prop != NULL) {
1531 if (format->digit != NULL) xmlFree(format->digit);
1532 format->digit = prop;
1535 prop = xmlGetNsProp(cur, (const xmlChar *)"pattern-separator", NULL);
1536 if (prop != NULL) {
1537 if (format->patternSeparator != NULL) xmlFree(format->patternSeparator);
1538 format->patternSeparator = prop;
1540 if (cur->children != NULL) {
1541 xsltParseContentError(style, cur->children);
1546 * xsltParseStylesheetPreserveSpace:
1547 * @style: the XSLT stylesheet
1548 * @cur: the "preserve-space" element
1550 * parse an XSLT stylesheet preserve-space element and record
1551 * elements needing preserving
1554 static void
1555 xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
1556 xmlChar *elements;
1557 xmlChar *element, *end;
1559 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1560 return;
1562 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
1563 if (elements == NULL) {
1564 xsltTransformError(NULL, style, cur,
1565 "xsltParseStylesheetPreserveSpace: missing elements attribute\n");
1566 if (style != NULL) style->warnings++;
1567 return;
1570 if (style->stripSpaces == NULL)
1571 style->stripSpaces = xmlHashCreate(10);
1572 if (style->stripSpaces == NULL)
1573 return;
1575 element = elements;
1576 while (*element != 0) {
1577 while (IS_BLANK(*element)) element++;
1578 if (*element == 0)
1579 break;
1580 end = element;
1581 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1582 element = xmlStrndup(element, end - element);
1583 if (element) {
1584 #ifdef WITH_XSLT_DEBUG_PARSING
1585 xsltGenericDebug(xsltGenericDebugContext,
1586 "add preserved space element %s\n", element);
1587 #endif
1588 if (xmlStrEqual(element, (const xmlChar *)"*")) {
1589 style->stripAll = -1;
1590 } else {
1591 const xmlChar *URI;
1594 * TODO: Don't use xsltGetQNameURI().
1596 URI = xsltGetQNameURI(cur, &element);
1598 xmlHashAddEntry2(style->stripSpaces, element, URI,
1599 (xmlChar *) "preserve");
1601 xmlFree(element);
1603 element = end;
1605 xmlFree(elements);
1606 if (cur->children != NULL) {
1607 xsltParseContentError(style, cur->children);
1611 #ifdef XSLT_REFACTORED
1612 #else
1614 * xsltParseStylesheetExtPrefix:
1615 * @style: the XSLT stylesheet
1616 * @template: the "extension-element-prefixes" prefix
1618 * parse an XSLT stylesheet's "extension-element-prefix" attribute value
1619 * and register the namespaces of extension instruction.
1620 * SPEC "A namespace is designated as an extension namespace by using
1621 * an extension-element-prefixes attribute on:
1622 * 1) an xsl:stylesheet element
1623 * 2) an xsl:extension-element-prefixes attribute on a
1624 * literal result element
1625 * 3) an extension instruction."
1627 static void
1628 xsltParseStylesheetExtPrefix(xsltStylesheetPtr style, xmlNodePtr cur,
1629 int isXsltElem) {
1630 xmlChar *prefixes;
1631 xmlChar *prefix, *end;
1633 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1634 return;
1636 if (isXsltElem) {
1637 /* For xsl:stylesheet/xsl:transform. */
1638 prefixes = xmlGetNsProp(cur,
1639 (const xmlChar *)"extension-element-prefixes", NULL);
1640 } else {
1641 /* For literal result elements and extension instructions. */
1642 prefixes = xmlGetNsProp(cur,
1643 (const xmlChar *)"extension-element-prefixes", XSLT_NAMESPACE);
1645 if (prefixes == NULL) {
1646 return;
1649 prefix = prefixes;
1650 while (*prefix != 0) {
1651 while (IS_BLANK(*prefix)) prefix++;
1652 if (*prefix == 0)
1653 break;
1654 end = prefix;
1655 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1656 prefix = xmlStrndup(prefix, end - prefix);
1657 if (prefix) {
1658 xmlNsPtr ns;
1660 if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
1661 ns = xmlSearchNs(style->doc, cur, NULL);
1662 else
1663 ns = xmlSearchNs(style->doc, cur, prefix);
1664 if (ns == NULL) {
1665 xsltTransformError(NULL, style, cur,
1666 "xsl:extension-element-prefix : undefined namespace %s\n",
1667 prefix);
1668 if (style != NULL) style->warnings++;
1669 } else {
1670 #ifdef WITH_XSLT_DEBUG_PARSING
1671 xsltGenericDebug(xsltGenericDebugContext,
1672 "add extension prefix %s\n", prefix);
1673 #endif
1674 xsltRegisterExtPrefix(style, prefix, ns->href);
1676 xmlFree(prefix);
1678 prefix = end;
1680 xmlFree(prefixes);
1682 #endif /* else of XSLT_REFACTORED */
1685 * xsltParseStylesheetStripSpace:
1686 * @style: the XSLT stylesheet
1687 * @cur: the "strip-space" element
1689 * parse an XSLT stylesheet's strip-space element and record
1690 * the elements needing stripping
1693 static void
1694 xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
1695 xmlChar *elements;
1696 xmlChar *element, *end;
1698 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1699 return;
1701 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", NULL);
1702 if (elements == NULL) {
1703 xsltTransformError(NULL, style, cur,
1704 "xsltParseStylesheetStripSpace: missing elements attribute\n");
1705 if (style != NULL) style->warnings++;
1706 return;
1709 if (style->stripSpaces == NULL)
1710 style->stripSpaces = xmlHashCreate(10);
1711 if (style->stripSpaces == NULL)
1712 return;
1714 element = elements;
1715 while (*element != 0) {
1716 while (IS_BLANK(*element)) element++;
1717 if (*element == 0)
1718 break;
1719 end = element;
1720 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1721 element = xmlStrndup(element, end - element);
1722 if (element) {
1723 #ifdef WITH_XSLT_DEBUG_PARSING
1724 xsltGenericDebug(xsltGenericDebugContext,
1725 "add stripped space element %s\n", element);
1726 #endif
1727 if (xmlStrEqual(element, (const xmlChar *)"*")) {
1728 style->stripAll = 1;
1729 } else {
1730 const xmlChar *URI;
1733 * TODO: Don't use xsltGetQNameURI().
1735 URI = xsltGetQNameURI(cur, &element);
1737 xmlHashAddEntry2(style->stripSpaces, element, URI,
1738 (xmlChar *) "strip");
1740 xmlFree(element);
1742 element = end;
1744 xmlFree(elements);
1745 if (cur->children != NULL) {
1746 xsltParseContentError(style, cur->children);
1750 #ifdef XSLT_REFACTORED
1751 #else
1753 * xsltParseStylesheetExcludePrefix:
1754 * @style: the XSLT stylesheet
1755 * @cur: the current point in the stylesheet
1757 * parse an XSLT stylesheet exclude prefix and record
1758 * namespaces needing stripping
1760 * Returns the number of Excluded prefixes added at that level
1763 static int
1764 xsltParseStylesheetExcludePrefix(xsltStylesheetPtr style, xmlNodePtr cur,
1765 int isXsltElem)
1767 int nb = 0;
1768 xmlChar *prefixes;
1769 xmlChar *prefix, *end;
1771 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1772 return(0);
1774 if (isXsltElem)
1775 prefixes = xmlGetNsProp(cur,
1776 (const xmlChar *)"exclude-result-prefixes", NULL);
1777 else
1778 prefixes = xmlGetNsProp(cur,
1779 (const xmlChar *)"exclude-result-prefixes", XSLT_NAMESPACE);
1781 if (prefixes == NULL) {
1782 return(0);
1785 prefix = prefixes;
1786 while (*prefix != 0) {
1787 while (IS_BLANK(*prefix)) prefix++;
1788 if (*prefix == 0)
1789 break;
1790 end = prefix;
1791 while ((*end != 0) && (!IS_BLANK(*end))) end++;
1792 prefix = xmlStrndup(prefix, end - prefix);
1793 if (prefix) {
1794 xmlNsPtr ns;
1796 if (xmlStrEqual(prefix, (const xmlChar *)"#default"))
1797 ns = xmlSearchNs(style->doc, cur, NULL);
1798 else
1799 ns = xmlSearchNs(style->doc, cur, prefix);
1800 if (ns == NULL) {
1801 xsltTransformError(NULL, style, cur,
1802 "xsl:exclude-result-prefixes : undefined namespace %s\n",
1803 prefix);
1804 if (style != NULL) style->warnings++;
1805 } else {
1806 if (exclPrefixPush(style, (xmlChar *) ns->href) >= 0) {
1807 #ifdef WITH_XSLT_DEBUG_PARSING
1808 xsltGenericDebug(xsltGenericDebugContext,
1809 "exclude result prefix %s\n", prefix);
1810 #endif
1811 nb++;
1814 xmlFree(prefix);
1816 prefix = end;
1818 xmlFree(prefixes);
1819 return(nb);
1821 #endif /* else of XSLT_REFACTORED */
1823 #ifdef XSLT_REFACTORED
1826 * xsltTreeEnsureXMLDecl:
1827 * @doc: the doc
1829 * BIG NOTE:
1830 * This was copy&pasted from Libxml2's xmlTreeEnsureXMLDecl() in "tree.c".
1831 * Ensures that there is an XML namespace declaration on the doc.
1833 * Returns the XML ns-struct or NULL on API and internal errors.
1835 static xmlNsPtr
1836 xsltTreeEnsureXMLDecl(xmlDocPtr doc)
1838 if (doc == NULL)
1839 return (NULL);
1840 if (doc->oldNs != NULL)
1841 return (doc->oldNs);
1843 xmlNsPtr ns;
1844 ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
1845 if (ns == NULL) {
1846 xmlGenericError(xmlGenericErrorContext,
1847 "xsltTreeEnsureXMLDecl: Failed to allocate "
1848 "the XML namespace.\n");
1849 return (NULL);
1851 memset(ns, 0, sizeof(xmlNs));
1852 ns->type = XML_LOCAL_NAMESPACE;
1854 * URGENT TODO: revisit this.
1856 #ifdef LIBXML_NAMESPACE_DICT
1857 if (doc->dict)
1858 ns->href = xmlDictLookup(doc->dict, XML_XML_NAMESPACE, -1);
1859 else
1860 ns->href = xmlStrdup(XML_XML_NAMESPACE);
1861 #else
1862 ns->href = xmlStrdup(XML_XML_NAMESPACE);
1863 #endif
1864 ns->prefix = xmlStrdup((const xmlChar *)"xml");
1865 doc->oldNs = ns;
1866 return (ns);
1871 * xsltTreeAcquireStoredNs:
1872 * @doc: the doc
1873 * @nsName: the namespace name
1874 * @prefix: the prefix
1876 * BIG NOTE:
1877 * This was copy&pasted from Libxml2's xmlDOMWrapStoreNs() in "tree.c".
1878 * Creates or reuses an xmlNs struct on doc->oldNs with
1879 * the given prefix and namespace name.
1881 * Returns the aquired ns struct or NULL in case of an API
1882 * or internal error.
1884 static xmlNsPtr
1885 xsltTreeAcquireStoredNs(xmlDocPtr doc,
1886 const xmlChar *nsName,
1887 const xmlChar *prefix)
1889 xmlNsPtr ns;
1891 if (doc == NULL)
1892 return (NULL);
1893 if (doc->oldNs != NULL)
1894 ns = doc->oldNs;
1895 else
1896 ns = xsltTreeEnsureXMLDecl(doc);
1897 if (ns == NULL)
1898 return (NULL);
1899 if (ns->next != NULL) {
1900 /* Reuse. */
1901 ns = ns->next;
1902 while (ns != NULL) {
1903 if ((ns->prefix == NULL) != (prefix == NULL)) {
1904 /* NOP */
1905 } else if (prefix == NULL) {
1906 if (xmlStrEqual(ns->href, nsName))
1907 return (ns);
1908 } else {
1909 if ((ns->prefix[0] == prefix[0]) &&
1910 xmlStrEqual(ns->prefix, prefix) &&
1911 xmlStrEqual(ns->href, nsName))
1912 return (ns);
1915 if (ns->next == NULL)
1916 break;
1917 ns = ns->next;
1920 /* Create. */
1921 ns->next = xmlNewNs(NULL, nsName, prefix);
1922 return (ns->next);
1926 * xsltLREBuildEffectiveNs:
1928 * Apply ns-aliasing on the namespace of the given @elem and
1929 * its attributes.
1931 static int
1932 xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt,
1933 xmlNodePtr elem)
1935 xmlNsPtr ns;
1936 xsltNsAliasPtr alias;
1938 if ((cctxt == NULL) || (elem == NULL))
1939 return(-1);
1940 if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases))
1941 return(0);
1943 alias = cctxt->nsAliases;
1944 while (alias != NULL) {
1945 if ( /* If both namespaces are NULL... */
1946 ( (elem->ns == NULL) &&
1947 ((alias->literalNs == NULL) ||
1948 (alias->literalNs->href == NULL)) ) ||
1949 /* ... or both namespace are equal */
1950 ( (elem->ns != NULL) &&
1951 (alias->literalNs != NULL) &&
1952 xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
1954 if ((alias->targetNs != NULL) &&
1955 (alias->targetNs->href != NULL))
1958 * Convert namespace.
1960 if (elem->doc == alias->docOfTargetNs) {
1962 * This is the nice case: same docs.
1963 * This will eventually assign a ns-decl which
1964 * is shadowed, but this has no negative effect on
1965 * the generation of the result tree.
1967 elem->ns = alias->targetNs;
1968 } else {
1970 * This target xmlNs originates from a different
1971 * stylesheet tree. Try to locate it in the
1972 * in-scope namespaces.
1973 * OPTIMIZE TODO: Use the compiler-node-info inScopeNs.
1975 ns = xmlSearchNs(elem->doc, elem,
1976 alias->targetNs->prefix);
1978 * If no matching ns-decl found, then assign a
1979 * ns-decl stored in xmlDoc.
1981 if ((ns == NULL) ||
1982 (! xmlStrEqual(ns->href, alias->targetNs->href)))
1985 * BIG NOTE: The use of xsltTreeAcquireStoredNs()
1986 * is not very efficient, but currently I don't
1987 * see an other way of *safely* changing a node's
1988 * namespace, since the xmlNs struct in
1989 * alias->targetNs might come from an other
1990 * stylesheet tree. So we need to anchor it in the
1991 * current document, without adding it to the tree,
1992 * which would otherwise change the in-scope-ns
1993 * semantic of the tree.
1995 ns = xsltTreeAcquireStoredNs(elem->doc,
1996 alias->targetNs->href,
1997 alias->targetNs->prefix);
1999 if (ns == NULL) {
2000 xsltTransformError(NULL, cctxt->style, elem,
2001 "Internal error in "
2002 "xsltLREBuildEffectiveNs(): "
2003 "failed to acquire a stored "
2004 "ns-declaration.\n");
2005 cctxt->style->errors++;
2006 return(-1);
2010 elem->ns = ns;
2012 } else {
2014 * Move into or leave in the NULL namespace.
2016 elem->ns = NULL;
2018 break;
2020 alias = alias->next;
2023 * Same with attributes of literal result elements.
2025 if (elem->properties != NULL) {
2026 xmlAttrPtr attr = elem->properties;
2028 while (attr != NULL) {
2029 if (attr->ns == NULL) {
2030 attr = attr->next;
2031 continue;
2033 alias = cctxt->nsAliases;
2034 while (alias != NULL) {
2035 if ( /* If both namespaces are NULL... */
2036 ( (elem->ns == NULL) &&
2037 ((alias->literalNs == NULL) ||
2038 (alias->literalNs->href == NULL)) ) ||
2039 /* ... or both namespace are equal */
2040 ( (elem->ns != NULL) &&
2041 (alias->literalNs != NULL) &&
2042 xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
2044 if ((alias->targetNs != NULL) &&
2045 (alias->targetNs->href != NULL))
2047 if (elem->doc == alias->docOfTargetNs) {
2048 elem->ns = alias->targetNs;
2049 } else {
2050 ns = xmlSearchNs(elem->doc, elem,
2051 alias->targetNs->prefix);
2052 if ((ns == NULL) ||
2053 (! xmlStrEqual(ns->href, alias->targetNs->href)))
2055 ns = xsltTreeAcquireStoredNs(elem->doc,
2056 alias->targetNs->href,
2057 alias->targetNs->prefix);
2059 if (ns == NULL) {
2060 xsltTransformError(NULL, cctxt->style, elem,
2061 "Internal error in "
2062 "xsltLREBuildEffectiveNs(): "
2063 "failed to acquire a stored "
2064 "ns-declaration.\n");
2065 cctxt->style->errors++;
2066 return(-1);
2070 elem->ns = ns;
2072 } else {
2074 * Move into or leave in the NULL namespace.
2076 elem->ns = NULL;
2078 break;
2080 alias = alias->next;
2083 attr = attr->next;
2086 return(0);
2090 * xsltLREBuildEffectiveNsNodes:
2092 * Computes the effective namespaces nodes for a literal result
2093 * element.
2094 * @effectiveNs is the set of effective ns-nodes
2095 * on the literal result element, which will be added to the result
2096 * element if not already existing in the result tree.
2097 * This means that excluded namespaces (via exclude-result-prefixes,
2098 * extension-element-prefixes and the XSLT namespace) not added
2099 * to the set.
2100 * Namespace-aliasing was applied on the @effectiveNs.
2102 static int
2103 xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
2104 xsltStyleItemLRElementInfoPtr item,
2105 xmlNodePtr elem,
2106 int isLRE)
2108 xmlNsPtr ns, tmpns;
2109 xsltEffectiveNsPtr effNs, lastEffNs = NULL;
2110 int i, j, holdByElem;
2111 xsltPointerListPtr extElemNs = cctxt->inode->extElemNs;
2112 xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs;
2114 if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) ||
2115 (item == NULL) || (item->effectiveNs != NULL))
2116 return(-1);
2118 if (item->inScopeNs == NULL)
2119 return(0);
2121 extElemNs = cctxt->inode->extElemNs;
2122 exclResultNs = cctxt->inode->exclResultNs;
2124 for (i = 0; i < item->inScopeNs->totalNumber; i++) {
2125 ns = item->inScopeNs->list[i];
2127 * Skip namespaces designated as excluded namespaces
2128 * -------------------------------------------------
2130 * XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces
2131 * which are target namespaces of namespace-aliases
2132 * regardless if designated as excluded.
2134 * Exclude the XSLT namespace.
2136 if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
2137 goto skip_ns;
2140 * Apply namespace aliasing
2141 * ------------------------
2143 * SPEC XSLT 2.0
2144 * "- A namespace node whose string value is a literal namespace
2145 * URI is not copied to the result tree.
2146 * - A namespace node whose string value is a target namespace URI
2147 * is copied to the result tree, whether or not the URI
2148 * identifies an excluded namespace."
2150 * NOTE: The ns-aliasing machanism is non-cascading.
2151 * (checked with Saxon, Xalan and MSXML .NET).
2152 * URGENT TODO: is style->nsAliases the effective list of
2153 * ns-aliases, or do we need to lookup the whole
2154 * import-tree?
2155 * TODO: Get rid of import-tree lookup.
2157 if (cctxt->hasNsAliases) {
2158 xsltNsAliasPtr alias;
2160 * First check for being a target namespace.
2162 alias = cctxt->nsAliases;
2163 do {
2165 * TODO: Is xmlns="" handled already?
2167 if ((alias->targetNs != NULL) &&
2168 (xmlStrEqual(alias->targetNs->href, ns->href)))
2171 * Recognized as a target namespace; use it regardless
2172 * if excluded otherwise.
2174 goto add_effective_ns;
2176 alias = alias->next;
2177 } while (alias != NULL);
2179 alias = cctxt->nsAliases;
2180 do {
2182 * TODO: Is xmlns="" handled already?
2184 if ((alias->literalNs != NULL) &&
2185 (xmlStrEqual(alias->literalNs->href, ns->href)))
2188 * Recognized as an namespace alias; do not use it.
2190 goto skip_ns;
2192 alias = alias->next;
2193 } while (alias != NULL);
2197 * Exclude excluded result namespaces.
2199 if (exclResultNs) {
2200 for (j = 0; j < exclResultNs->number; j++)
2201 if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j]))
2202 goto skip_ns;
2205 * Exclude extension-element namespaces.
2207 if (extElemNs) {
2208 for (j = 0; j < extElemNs->number; j++)
2209 if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j]))
2210 goto skip_ns;
2213 add_effective_ns:
2215 * OPTIMIZE TODO: This information may not be needed.
2217 if (isLRE && (elem->nsDef != NULL)) {
2218 holdByElem = 0;
2219 tmpns = elem->nsDef;
2220 do {
2221 if (tmpns == ns) {
2222 holdByElem = 1;
2223 break;
2225 tmpns = tmpns->next;
2226 } while (tmpns != NULL);
2227 } else
2228 holdByElem = 0;
2232 * Add the effective namespace declaration.
2234 effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs));
2235 if (effNs == NULL) {
2236 xsltTransformError(NULL, cctxt->style, elem,
2237 "Internal error in xsltLREBuildEffectiveNs(): "
2238 "failed to allocate memory.\n");
2239 cctxt->style->errors++;
2240 return(-1);
2242 if (cctxt->psData->effectiveNs == NULL) {
2243 cctxt->psData->effectiveNs = effNs;
2244 effNs->nextInStore = NULL;
2245 } else {
2246 effNs->nextInStore = cctxt->psData->effectiveNs;
2247 cctxt->psData->effectiveNs = effNs;
2250 effNs->next = NULL;
2251 effNs->prefix = ns->prefix;
2252 effNs->nsName = ns->href;
2253 effNs->holdByElem = holdByElem;
2255 if (lastEffNs == NULL)
2256 item->effectiveNs = effNs;
2257 else
2258 lastEffNs->next = effNs;
2259 lastEffNs = effNs;
2261 skip_ns:
2264 return(0);
2269 * xsltLREInfoCreate:
2271 * @isLRE: indicates if the given @elem is a literal result element
2273 * Creates a new info for a literal result element.
2275 static int
2276 xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
2277 xmlNodePtr elem,
2278 int isLRE)
2280 xsltStyleItemLRElementInfoPtr item;
2282 if ((cctxt == NULL) || (cctxt->inode == NULL))
2283 return(-1);
2285 item = (xsltStyleItemLRElementInfoPtr)
2286 xmlMalloc(sizeof(xsltStyleItemLRElementInfo));
2287 if (item == NULL) {
2288 xsltTransformError(NULL, cctxt->style, NULL,
2289 "Internal error in xsltLREInfoCreate(): "
2290 "memory allocation failed.\n");
2291 cctxt->style->errors++;
2292 return(-1);
2294 memset(item, 0, sizeof(xsltStyleItemLRElementInfo));
2295 item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT;
2297 * Store it in the stylesheet.
2299 item->next = cctxt->style->preComps;
2300 cctxt->style->preComps = (xsltElemPreCompPtr) item;
2302 * @inScopeNs are used for execution of XPath expressions
2303 * in AVTs.
2305 item->inScopeNs = cctxt->inode->inScopeNs;
2307 if (elem)
2308 xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE);
2310 cctxt->inode->litResElemInfo = item;
2311 cctxt->inode->nsChanged = 0;
2312 cctxt->maxLREs++;
2313 return(0);
2317 * xsltCompilerVarInfoPush:
2318 * @cctxt: the compilation context
2320 * Pushes a new var/param info onto the stack.
2322 * Returns the acquired variable info.
2324 static xsltVarInfoPtr
2325 xsltCompilerVarInfoPush(xsltCompilerCtxtPtr cctxt,
2326 xmlNodePtr inst,
2327 const xmlChar *name,
2328 const xmlChar *nsName)
2330 xsltVarInfoPtr ivar;
2332 if ((cctxt->ivar != NULL) && (cctxt->ivar->next != NULL)) {
2333 ivar = cctxt->ivar->next;
2334 } else if ((cctxt->ivar == NULL) && (cctxt->ivars != NULL)) {
2335 ivar = cctxt->ivars;
2336 } else {
2337 ivar = (xsltVarInfoPtr) xmlMalloc(sizeof(xsltVarInfo));
2338 if (ivar == NULL) {
2339 xsltTransformError(NULL, cctxt->style, inst,
2340 "xsltParseInScopeVarPush: xmlMalloc() failed!\n");
2341 cctxt->style->errors++;
2342 return(NULL);
2344 /* memset(retVar, 0, sizeof(xsltInScopeVar)); */
2345 if (cctxt->ivars == NULL) {
2346 cctxt->ivars = ivar;
2347 ivar->prev = NULL;
2348 } else {
2349 cctxt->ivar->next = ivar;
2350 ivar->prev = cctxt->ivar;
2352 cctxt->ivar = ivar;
2353 ivar->next = NULL;
2355 ivar->depth = cctxt->depth;
2356 ivar->name = name;
2357 ivar->nsName = nsName;
2358 return(ivar);
2362 * xsltCompilerVarInfoPop:
2363 * @cctxt: the compilation context
2365 * Pops all var/param infos from the stack, which
2366 * have the current depth.
2368 static void
2369 xsltCompilerVarInfoPop(xsltCompilerCtxtPtr cctxt)
2372 while ((cctxt->ivar != NULL) &&
2373 (cctxt->ivar->depth > cctxt->depth))
2375 cctxt->ivar = cctxt->ivar->prev;
2380 * xsltCompilerNodePush:
2382 * @cctxt: the compilation context
2383 * @node: the node to be pushed (this can also be the doc-node)
2387 * Returns the current node info structure or
2388 * NULL in case of an internal error.
2390 static xsltCompilerNodeInfoPtr
2391 xsltCompilerNodePush(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
2393 xsltCompilerNodeInfoPtr inode, iprev;
2395 if ((cctxt->inode != NULL) && (cctxt->inode->next != NULL)) {
2396 inode = cctxt->inode->next;
2397 } else if ((cctxt->inode == NULL) && (cctxt->inodeList != NULL)) {
2398 inode = cctxt->inodeList;
2399 } else {
2401 * Create a new node-info.
2403 inode = (xsltCompilerNodeInfoPtr)
2404 xmlMalloc(sizeof(xsltCompilerNodeInfo));
2405 if (inode == NULL) {
2406 xsltTransformError(NULL, cctxt->style, NULL,
2407 "xsltCompilerNodePush: malloc failed.\n");
2408 return(NULL);
2410 memset(inode, 0, sizeof(xsltCompilerNodeInfo));
2411 if (cctxt->inodeList == NULL)
2412 cctxt->inodeList = inode;
2413 else {
2414 cctxt->inodeLast->next = inode;
2415 inode->prev = cctxt->inodeLast;
2417 cctxt->inodeLast = inode;
2418 cctxt->maxNodeInfos++;
2419 if (cctxt->inode == NULL) {
2420 cctxt->inode = inode;
2422 * Create an initial literal result element info for
2423 * the root of the stylesheet.
2425 xsltLREInfoCreate(cctxt, NULL, 0);
2428 cctxt->depth++;
2429 cctxt->inode = inode;
2431 * REVISIT TODO: Keep the reset always complete.
2432 * NOTE: Be carefull with the @node, since it might be
2433 * a doc-node.
2435 inode->node = node;
2436 inode->depth = cctxt->depth;
2437 inode->templ = NULL;
2438 inode->category = XSLT_ELEMENT_CATEGORY_XSLT;
2439 inode->type = 0;
2440 inode->item = NULL;
2441 inode->curChildType = 0;
2442 inode->extContentHandled = 0;
2443 inode->isRoot = 0;
2445 if (inode->prev != NULL) {
2446 iprev = inode->prev;
2448 * Inherit the following information:
2449 * ---------------------------------
2451 * In-scope namespaces
2453 inode->inScopeNs = iprev->inScopeNs;
2455 * Info for literal result elements
2457 inode->litResElemInfo = iprev->litResElemInfo;
2458 inode->nsChanged = iprev->nsChanged;
2460 * Excluded result namespaces
2462 inode->exclResultNs = iprev->exclResultNs;
2464 * Extension instruction namespaces
2466 inode->extElemNs = iprev->extElemNs;
2468 * Whitespace preservation
2470 inode->preserveWhitespace = iprev->preserveWhitespace;
2472 * Forwards-compatible mode
2474 inode->forwardsCompat = iprev->forwardsCompat;
2475 } else {
2476 inode->inScopeNs = NULL;
2477 inode->exclResultNs = NULL;
2478 inode->extElemNs = NULL;
2479 inode->preserveWhitespace = 0;
2480 inode->forwardsCompat = 0;
2483 return(inode);
2487 * xsltCompilerNodePop:
2489 * @cctxt: the compilation context
2490 * @node: the node to be pushed (this can also be the doc-node)
2492 * Pops the current node info.
2494 static void
2495 xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
2497 if (cctxt->inode == NULL) {
2498 xmlGenericError(xmlGenericErrorContext,
2499 "xsltCompilerNodePop: Top-node mismatch.\n");
2500 return;
2503 * NOTE: Be carefull with the @node, since it might be
2504 * a doc-node.
2506 if (cctxt->inode->node != node) {
2507 xmlGenericError(xmlGenericErrorContext,
2508 "xsltCompilerNodePop: Node mismatch.\n");
2509 goto mismatch;
2511 if (cctxt->inode->depth != cctxt->depth) {
2512 xmlGenericError(xmlGenericErrorContext,
2513 "xsltCompilerNodePop: Depth mismatch.\n");
2514 goto mismatch;
2516 cctxt->depth--;
2518 * Pop information of variables.
2520 if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth))
2521 xsltCompilerVarInfoPop(cctxt);
2523 cctxt->inode = cctxt->inode->prev;
2524 if (cctxt->inode != NULL)
2525 cctxt->inode->curChildType = 0;
2526 return;
2528 mismatch:
2530 const xmlChar *nsName = NULL, *name = NULL;
2531 const xmlChar *infnsName = NULL, *infname = NULL;
2533 if (node) {
2534 if (node->type == XML_ELEMENT_NODE) {
2535 name = node->name;
2536 if (node->ns != NULL)
2537 nsName = node->ns->href;
2538 else
2539 nsName = BAD_CAST "";
2540 } else {
2541 name = BAD_CAST "#document";
2542 nsName = BAD_CAST "";
2544 } else
2545 name = BAD_CAST "Not given";
2547 if (cctxt->inode->node) {
2548 if (node->type == XML_ELEMENT_NODE) {
2549 infname = cctxt->inode->node->name;
2550 if (cctxt->inode->node->ns != NULL)
2551 infnsName = cctxt->inode->node->ns->href;
2552 else
2553 infnsName = BAD_CAST "";
2554 } else {
2555 infname = BAD_CAST "#document";
2556 infnsName = BAD_CAST "";
2558 } else
2559 infname = BAD_CAST "Not given";
2562 xmlGenericError(xmlGenericErrorContext,
2563 "xsltCompilerNodePop: Given : '%s' URI '%s'\n",
2564 name, nsName);
2565 xmlGenericError(xmlGenericErrorContext,
2566 "xsltCompilerNodePop: Expected: '%s' URI '%s'\n",
2567 infname, infnsName);
2572 * xsltCompilerBuildInScopeNsList:
2574 * Create and store the list of in-scope namespaces for the given
2575 * node in the stylesheet. If there are no changes in the in-scope
2576 * namespaces then the last ns-info of the ancestor axis will be returned.
2577 * Compilation-time only.
2579 * Returns the ns-info or NULL if there are no namespaces in scope.
2581 static xsltNsListContainerPtr
2582 xsltCompilerBuildInScopeNsList(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
2584 xsltNsListContainerPtr nsi = NULL;
2585 xmlNsPtr *list = NULL, ns;
2586 int i, maxns = 5;
2588 * Create a new ns-list for this position in the node-tree.
2589 * xmlGetNsList() will return NULL, if there are no ns-decls in the
2590 * tree. Note that the ns-decl for the XML namespace is not added
2591 * to the resulting list; the XPath module handles the XML namespace
2592 * internally.
2594 while (node != NULL) {
2595 if (node->type == XML_ELEMENT_NODE) {
2596 ns = node->nsDef;
2597 while (ns != NULL) {
2598 if (nsi == NULL) {
2599 nsi = (xsltNsListContainerPtr)
2600 xmlMalloc(sizeof(xsltNsListContainer));
2601 if (nsi == NULL) {
2602 xsltTransformError(NULL, cctxt->style, NULL,
2603 "xsltCompilerBuildInScopeNsList: "
2604 "malloc failed!\n");
2605 goto internal_err;
2607 memset(nsi, 0, sizeof(xsltNsListContainer));
2608 nsi->list =
2609 (xmlNsPtr *) xmlMalloc(maxns * sizeof(xmlNsPtr));
2610 if (nsi->list == NULL) {
2611 xsltTransformError(NULL, cctxt->style, NULL,
2612 "xsltCompilerBuildInScopeNsList: "
2613 "malloc failed!\n");
2614 goto internal_err;
2616 nsi->list[0] = NULL;
2619 * Skip shadowed namespace bindings.
2621 for (i = 0; i < nsi->totalNumber; i++) {
2622 if ((ns->prefix == nsi->list[i]->prefix) ||
2623 (xmlStrEqual(ns->prefix, nsi->list[i]->prefix)))
2624 break;
2626 if (i >= nsi->totalNumber) {
2627 if (nsi->totalNumber +1 >= maxns) {
2628 maxns *= 2;
2629 nsi->list =
2630 (xmlNsPtr *) xmlRealloc(nsi->list,
2631 maxns * sizeof(xmlNsPtr));
2632 if (nsi->list == NULL) {
2633 xsltTransformError(NULL, cctxt->style, NULL,
2634 "xsltCompilerBuildInScopeNsList: "
2635 "realloc failed!\n");
2636 goto internal_err;
2639 nsi->list[nsi->totalNumber++] = ns;
2640 nsi->list[nsi->totalNumber] = NULL;
2643 ns = ns->next;
2646 node = node->parent;
2648 if (nsi == NULL)
2649 return(NULL);
2651 * Move the default namespace to last position.
2653 nsi->xpathNumber = nsi->totalNumber;
2654 for (i = 0; i < nsi->totalNumber; i++) {
2655 if (nsi->list[i]->prefix == NULL) {
2656 ns = nsi->list[i];
2657 nsi->list[i] = nsi->list[nsi->totalNumber-1];
2658 nsi->list[nsi->totalNumber-1] = ns;
2659 nsi->xpathNumber--;
2660 break;
2664 * Store the ns-list in the stylesheet.
2666 if (xsltPointerListAddSize(
2667 (xsltPointerListPtr)cctxt->psData->inScopeNamespaces,
2668 (void *) nsi, 5) == -1)
2670 xmlFree(nsi);
2671 nsi = NULL;
2672 xsltTransformError(NULL, cctxt->style, NULL,
2673 "xsltCompilerBuildInScopeNsList: failed to add ns-info.\n");
2674 goto internal_err;
2677 * Notify of change in status wrt namespaces.
2679 if (cctxt->inode != NULL)
2680 cctxt->inode->nsChanged = 1;
2682 return(nsi);
2684 internal_err:
2685 if (list != NULL)
2686 xmlFree(list);
2687 cctxt->style->errors++;
2688 return(NULL);
2691 static int
2692 xsltParseNsPrefixList(xsltCompilerCtxtPtr cctxt,
2693 xsltPointerListPtr list,
2694 xmlNodePtr node,
2695 const xmlChar *value)
2697 xmlChar *cur, *end;
2698 xmlNsPtr ns;
2700 if ((cctxt == NULL) || (value == NULL) || (list == NULL))
2701 return(-1);
2703 list->number = 0;
2705 cur = (xmlChar *) value;
2706 while (*cur != 0) {
2707 while (IS_BLANK(*cur)) cur++;
2708 if (*cur == 0)
2709 break;
2710 end = cur;
2711 while ((*end != 0) && (!IS_BLANK(*end))) end++;
2712 cur = xmlStrndup(cur, end - cur);
2713 if (cur == NULL) {
2714 cur = end;
2715 continue;
2718 * TODO: Export and use xmlSearchNsByPrefixStrict()
2719 * in Libxml2, tree.c, since xmlSearchNs() is in most
2720 * cases not efficient and in some cases not correct.
2722 * XSLT-2 TODO: XSLT 2.0 allows an additional "#all" value.
2724 if ((cur[0] == '#') &&
2725 xmlStrEqual(cur, (const xmlChar *)"#default"))
2726 ns = xmlSearchNs(cctxt->style->doc, node, NULL);
2727 else
2728 ns = xmlSearchNs(cctxt->style->doc, node, cur);
2730 if (ns == NULL) {
2732 * TODO: Better to report the attr-node, otherwise
2733 * the user won't know which attribute was invalid.
2735 xsltTransformError(NULL, cctxt->style, node,
2736 "No namespace binding in scope for prefix '%s'.\n", cur);
2738 * XSLT-1.0: "It is an error if there is no namespace
2739 * bound to the prefix on the element bearing the
2740 * exclude-result-prefixes or xsl:exclude-result-prefixes
2741 * attribute."
2743 cctxt->style->errors++;
2744 } else {
2745 #ifdef WITH_XSLT_DEBUG_PARSING
2746 xsltGenericDebug(xsltGenericDebugContext,
2747 "resolved prefix '%s'\n", cur);
2748 #endif
2750 * Note that we put the namespace name into the dict.
2752 if (xsltPointerListAddSize(list,
2753 (void *) xmlDictLookup(cctxt->style->dict,
2754 ns->href, -1), 5) == -1)
2756 xmlFree(cur);
2757 goto internal_err;
2760 xmlFree(cur);
2762 cur = end;
2764 return(0);
2766 internal_err:
2767 cctxt->style->errors++;
2768 return(-1);
2772 * xsltCompilerUtilsCreateMergedList:
2773 * @dest: the destination list (optional)
2774 * @first: the first list
2775 * @second: the second list (optional)
2777 * Appends the content of @second to @first into @destination.
2778 * If @destination is NULL a new list will be created.
2780 * Returns the merged list of items or NULL if there's nothing to merge.
2782 static xsltPointerListPtr
2783 xsltCompilerUtilsCreateMergedList(xsltPointerListPtr first,
2784 xsltPointerListPtr second)
2786 xsltPointerListPtr ret;
2787 size_t num;
2789 if (first)
2790 num = first->number;
2791 else
2792 num = 0;
2793 if (second)
2794 num += second->number;
2795 if (num == 0)
2796 return(NULL);
2797 ret = xsltPointerListCreate(num);
2798 if (ret == NULL)
2799 return(NULL);
2801 * Copy contents.
2803 if ((first != NULL) && (first->number != 0)) {
2804 memcpy(ret->items, first->items,
2805 first->number * sizeof(void *));
2806 if ((second != NULL) && (second->number != 0))
2807 memcpy(ret->items + first->number, second->items,
2808 second->number * sizeof(void *));
2809 } else if ((second != NULL) && (second->number != 0))
2810 memcpy(ret->items, (void *) second->items,
2811 second->number * sizeof(void *));
2812 ret->number = num;
2813 return(ret);
2817 * xsltParseExclResultPrefixes:
2819 * Create and store the list of in-scope namespaces for the given
2820 * node in the stylesheet. If there are no changes in the in-scope
2821 * namespaces then the last ns-info of the ancestor axis will be returned.
2822 * Compilation-time only.
2824 * Returns the ns-info or NULL if there are no namespaces in scope.
2826 static xsltPointerListPtr
2827 xsltParseExclResultPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
2828 xsltPointerListPtr def,
2829 int instrCategory)
2831 xsltPointerListPtr list = NULL;
2832 xmlChar *value;
2833 xmlAttrPtr attr;
2835 if ((cctxt == NULL) || (node == NULL))
2836 return(NULL);
2838 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
2839 attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes", NULL);
2840 else
2841 attr = xmlHasNsProp(node, BAD_CAST "exclude-result-prefixes",
2842 XSLT_NAMESPACE);
2843 if (attr == NULL)
2844 return(def);
2846 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
2848 * Mark the XSLT attr.
2850 attr->psvi = (void *) xsltXSLTAttrMarker;
2853 if ((attr->children != NULL) &&
2854 (attr->children->content != NULL))
2855 value = attr->children->content;
2856 else {
2857 xsltTransformError(NULL, cctxt->style, node,
2858 "Attribute 'exclude-result-prefixes': Invalid value.\n");
2859 cctxt->style->errors++;
2860 return(def);
2863 if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
2864 BAD_CAST value) != 0)
2865 goto exit;
2866 if (cctxt->tmpList->number == 0)
2867 goto exit;
2869 * Merge the list with the inherited list.
2871 list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList);
2872 if (list == NULL)
2873 goto exit;
2875 * Store the list in the stylesheet/compiler context.
2877 if (xsltPointerListAddSize(
2878 cctxt->psData->exclResultNamespaces, list, 5) == -1)
2880 xsltPointerListFree(list);
2881 list = NULL;
2882 goto exit;
2885 * Notify of change in status wrt namespaces.
2887 if (cctxt->inode != NULL)
2888 cctxt->inode->nsChanged = 1;
2890 exit:
2891 if (list != NULL)
2892 return(list);
2893 else
2894 return(def);
2898 * xsltParseExtElemPrefixes:
2900 * Create and store the list of in-scope namespaces for the given
2901 * node in the stylesheet. If there are no changes in the in-scope
2902 * namespaces then the last ns-info of the ancestor axis will be returned.
2903 * Compilation-time only.
2905 * Returns the ns-info or NULL if there are no namespaces in scope.
2907 static xsltPointerListPtr
2908 xsltParseExtElemPrefixes(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
2909 xsltPointerListPtr def,
2910 int instrCategory)
2912 xsltPointerListPtr list = NULL;
2913 xmlAttrPtr attr;
2914 xmlChar *value;
2915 int i;
2917 if ((cctxt == NULL) || (node == NULL))
2918 return(NULL);
2920 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
2921 attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes", NULL);
2922 else
2923 attr = xmlHasNsProp(node, BAD_CAST "extension-element-prefixes",
2924 XSLT_NAMESPACE);
2925 if (attr == NULL)
2926 return(def);
2928 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
2930 * Mark the XSLT attr.
2932 attr->psvi = (void *) xsltXSLTAttrMarker;
2935 if ((attr->children != NULL) &&
2936 (attr->children->content != NULL))
2937 value = attr->children->content;
2938 else {
2939 xsltTransformError(NULL, cctxt->style, node,
2940 "Attribute 'extension-element-prefixes': Invalid value.\n");
2941 cctxt->style->errors++;
2942 return(def);
2946 if (xsltParseNsPrefixList(cctxt, cctxt->tmpList, node,
2947 BAD_CAST value) != 0)
2948 goto exit;
2950 if (cctxt->tmpList->number == 0)
2951 goto exit;
2953 * REVISIT: Register the extension namespaces.
2955 for (i = 0; i < cctxt->tmpList->number; i++)
2956 xsltRegisterExtPrefix(cctxt->style, NULL,
2957 BAD_CAST cctxt->tmpList->items[i]);
2959 * Merge the list with the inherited list.
2961 list = xsltCompilerUtilsCreateMergedList(def, cctxt->tmpList);
2962 if (list == NULL)
2963 goto exit;
2965 * Store the list in the stylesheet.
2967 if (xsltPointerListAddSize(
2968 cctxt->psData->extElemNamespaces, list, 5) == -1)
2970 xsltPointerListFree(list);
2971 list = NULL;
2972 goto exit;
2975 * Notify of change in status wrt namespaces.
2977 if (cctxt->inode != NULL)
2978 cctxt->inode->nsChanged = 1;
2980 exit:
2981 if (list != NULL)
2982 return(list);
2983 else
2984 return(def);
2988 * xsltParseAttrXSLTVersion:
2990 * @cctxt: the compilation context
2991 * @node: the element-node
2992 * @isXsltElem: whether this is an XSLT element
2994 * Parses the attribute xsl:version.
2996 * Returns 1 if there was such an attribute, 0 if not and
2997 * -1 if an internal or API error occured.
2999 static int
3000 xsltParseAttrXSLTVersion(xsltCompilerCtxtPtr cctxt, xmlNodePtr node,
3001 int instrCategory)
3003 xmlChar *value;
3004 xmlAttrPtr attr;
3006 if ((cctxt == NULL) || (node == NULL))
3007 return(-1);
3009 if (instrCategory == XSLT_ELEMENT_CATEGORY_XSLT)
3010 attr = xmlHasNsProp(node, BAD_CAST "version", NULL);
3011 else
3012 attr = xmlHasNsProp(node, BAD_CAST "version", XSLT_NAMESPACE);
3014 if (attr == NULL)
3015 return(0);
3017 attr->psvi = (void *) xsltXSLTAttrMarker;
3019 if ((attr->children != NULL) &&
3020 (attr->children->content != NULL))
3021 value = attr->children->content;
3022 else {
3023 xsltTransformError(NULL, cctxt->style, node,
3024 "Attribute 'version': Invalid value.\n");
3025 cctxt->style->errors++;
3026 return(1);
3029 if (! xmlStrEqual(value, (const xmlChar *)"1.0")) {
3030 cctxt->inode->forwardsCompat = 1;
3032 * TODO: To what extent do we support the
3033 * forwards-compatible mode?
3036 * Report this only once per compilation episode.
3038 if (! cctxt->hasForwardsCompat) {
3039 cctxt->hasForwardsCompat = 1;
3040 cctxt->errSeverity = XSLT_ERROR_SEVERITY_WARNING;
3041 xsltTransformError(NULL, cctxt->style, node,
3042 "Warning: the attribute xsl:version specifies a value "
3043 "different from '1.0'. Switching to forwards-compatible "
3044 "mode. Only features of XSLT 1.0 are supported by this "
3045 "processor.\n");
3046 cctxt->style->warnings++;
3047 cctxt->errSeverity = XSLT_ERROR_SEVERITY_ERROR;
3049 } else {
3050 cctxt->inode->forwardsCompat = 0;
3053 if (attr && (instrCategory == XSLT_ELEMENT_CATEGORY_LRE)) {
3055 * Set a marker on XSLT attributes.
3057 attr->psvi = (void *) xsltXSLTAttrMarker;
3059 return(1);
3062 static int
3063 xsltParsePreprocessStylesheetTree(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
3065 xmlNodePtr deleteNode, cur, txt, textNode = NULL;
3066 xmlDocPtr doc;
3067 xsltStylesheetPtr style;
3068 int internalize = 0, findSpaceAttr;
3069 int xsltStylesheetElemDepth;
3070 xmlAttrPtr attr;
3071 xmlChar *value;
3072 const xmlChar *name, *nsNameXSLT = NULL;
3073 int strictWhitespace, inXSLText = 0;
3074 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
3075 xsltNsMapPtr nsMapItem;
3076 #endif
3078 if ((cctxt == NULL) || (cctxt->style == NULL) ||
3079 (node == NULL) || (node->type != XML_ELEMENT_NODE))
3080 return(-1);
3082 doc = node->doc;
3083 if (doc == NULL)
3084 goto internal_err;
3086 style = cctxt->style;
3087 if ((style->dict != NULL) && (doc->dict == style->dict))
3088 internalize = 1;
3089 else
3090 style->internalized = 0;
3093 * Init value of xml:space. Since this might be an embedded
3094 * stylesheet, this is needed to be performed on the element
3095 * where the stylesheet is rooted at, taking xml:space of
3096 * ancestors into account.
3098 if (! cctxt->simplified)
3099 xsltStylesheetElemDepth = cctxt->depth +1;
3100 else
3101 xsltStylesheetElemDepth = 0;
3103 if (xmlNodeGetSpacePreserve(node) != 1)
3104 cctxt->inode->preserveWhitespace = 0;
3105 else
3106 cctxt->inode->preserveWhitespace = 1;
3109 * Eval if we should keep the old incorrect behaviour.
3111 strictWhitespace = (cctxt->strict != 0) ? 1 : 0;
3113 nsNameXSLT = xsltConstNamespaceNameXSLT;
3115 deleteNode = NULL;
3116 cur = node;
3117 while (cur != NULL) {
3118 if (deleteNode != NULL) {
3120 #ifdef WITH_XSLT_DEBUG_BLANKS
3121 xsltGenericDebug(xsltGenericDebugContext,
3122 "xsltParsePreprocessStylesheetTree: removing node\n");
3123 #endif
3124 xmlUnlinkNode(deleteNode);
3125 xmlFreeNode(deleteNode);
3126 deleteNode = NULL;
3128 if (cur->type == XML_ELEMENT_NODE) {
3131 * Clear the PSVI field.
3133 cur->psvi = NULL;
3135 xsltCompilerNodePush(cctxt, cur);
3137 inXSLText = 0;
3138 textNode = NULL;
3139 findSpaceAttr = 1;
3140 cctxt->inode->stripWhitespace = 0;
3142 * TODO: I'd love to use a string pointer comparison here :-/
3144 if (IS_XSLT_ELEM(cur)) {
3145 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
3146 if (cur->ns->href != nsNameXSLT) {
3147 nsMapItem = xsltNewNamespaceMapItem(cctxt,
3148 doc, cur->ns, cur);
3149 if (nsMapItem == NULL)
3150 goto internal_err;
3151 cur->ns->href = nsNameXSLT;
3153 #endif
3155 if (cur->name == NULL)
3156 goto process_attributes;
3158 * Mark the XSLT element for later recognition.
3159 * TODO: Using the marker is still too dangerous, since if
3160 * the parsing mechanism leaves out an XSLT element, then
3161 * this might hit the transformation-mechanism, which
3162 * will break if it doesn't expect such a marker.
3164 /* cur->psvi = (void *) xsltXSLTElemMarker; */
3167 * XSLT 2.0: "Any whitespace text node whose parent is
3168 * one of the following elements is removed from the "
3169 * tree, regardless of any xml:space attributes:..."
3170 * xsl:apply-imports,
3171 * xsl:apply-templates,
3172 * xsl:attribute-set,
3173 * xsl:call-template,
3174 * xsl:choose,
3175 * xsl:stylesheet, xsl:transform.
3176 * XSLT 2.0: xsl:analyze-string,
3177 * xsl:character-map,
3178 * xsl:next-match
3180 * TODO: I'd love to use a string pointer comparison here :-/
3182 name = cur->name;
3183 switch (*name) {
3184 case 't':
3185 if ((name[0] == 't') && (name[1] == 'e') &&
3186 (name[2] == 'x') && (name[3] == 't') &&
3187 (name[4] == 0))
3190 * Process the xsl:text element.
3191 * ----------------------------
3192 * Mark it for later recognition.
3194 cur->psvi = (void *) xsltXSLTTextMarker;
3196 * For stylesheets, the set of
3197 * whitespace-preserving element names
3198 * consists of just xsl:text.
3200 findSpaceAttr = 0;
3201 cctxt->inode->preserveWhitespace = 1;
3202 inXSLText = 1;
3204 break;
3205 case 'c':
3206 if (xmlStrEqual(name, BAD_CAST "choose") ||
3207 xmlStrEqual(name, BAD_CAST "call-template"))
3208 cctxt->inode->stripWhitespace = 1;
3209 break;
3210 case 'a':
3211 if (xmlStrEqual(name, BAD_CAST "apply-templates") ||
3212 xmlStrEqual(name, BAD_CAST "apply-imports") ||
3213 xmlStrEqual(name, BAD_CAST "attribute-set"))
3215 cctxt->inode->stripWhitespace = 1;
3216 break;
3217 default:
3218 if (xsltStylesheetElemDepth == cctxt->depth) {
3220 * This is a xsl:stylesheet/xsl:transform.
3222 cctxt->inode->stripWhitespace = 1;
3223 break;
3226 if ((cur->prev != NULL) &&
3227 (cur->prev->type == XML_TEXT_NODE))
3230 * XSLT 2.0 : "Any whitespace text node whose
3231 * following-sibling node is an xsl:param or
3232 * xsl:sort element is removed from the tree,
3233 * regardless of any xml:space attributes."
3235 if (((*name == 'p') || (*name == 's')) &&
3236 (xmlStrEqual(name, BAD_CAST "param") ||
3237 xmlStrEqual(name, BAD_CAST "sort")))
3239 do {
3240 if (IS_BLANK_NODE(cur->prev)) {
3241 txt = cur->prev;
3242 xmlUnlinkNode(txt);
3243 xmlFreeNode(txt);
3244 } else {
3246 * This will result in a content
3247 * error, when hitting the parsing
3248 * functions.
3250 break;
3252 } while (cur->prev);
3255 break;
3259 process_attributes:
3261 * Process attributes.
3262 * ------------------
3264 if (cur->properties != NULL) {
3265 if (cur->children == NULL)
3266 findSpaceAttr = 0;
3267 attr = cur->properties;
3268 do {
3269 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
3270 if ((attr->ns) && (attr->ns->href != nsNameXSLT) &&
3271 xmlStrEqual(attr->ns->href, nsNameXSLT))
3273 nsMapItem = xsltNewNamespaceMapItem(cctxt,
3274 doc, attr->ns, cur);
3275 if (nsMapItem == NULL)
3276 goto internal_err;
3277 attr->ns->href = nsNameXSLT;
3279 #endif
3280 if (internalize) {
3282 * Internalize the attribute's value; the goal is to
3283 * speed up operations and minimize used space by
3284 * compiled stylesheets.
3286 txt = attr->children;
3288 * NOTE that this assumes only one
3289 * text-node in the attribute's content.
3291 if ((txt != NULL) && (txt->content != NULL) &&
3292 (!xmlDictOwns(style->dict, txt->content)))
3294 value = (xmlChar *) xmlDictLookup(style->dict,
3295 txt->content, -1);
3296 xmlNodeSetContent(txt, NULL);
3297 txt->content = value;
3301 * Process xml:space attributes.
3302 * ----------------------------
3304 if ((findSpaceAttr != 0) &&
3305 (attr->ns != NULL) &&
3306 (attr->name != NULL) &&
3307 (attr->name[0] == 's') &&
3308 (attr->ns->prefix != NULL) &&
3309 (attr->ns->prefix[0] == 'x') &&
3310 (attr->ns->prefix[1] == 'm') &&
3311 (attr->ns->prefix[2] == 'l') &&
3312 (attr->ns->prefix[3] == 0))
3314 value = xmlGetNsProp(cur, BAD_CAST "space",
3315 XML_XML_NAMESPACE);
3316 if (value != NULL) {
3317 if (xmlStrEqual(value, BAD_CAST "preserve")) {
3318 cctxt->inode->preserveWhitespace = 1;
3319 } else if (xmlStrEqual(value, BAD_CAST "default")) {
3320 cctxt->inode->preserveWhitespace = 0;
3321 } else {
3322 /* Invalid value for xml:space. */
3323 xsltTransformError(NULL, style, cur,
3324 "Attribute xml:space: Invalid value.\n");
3325 cctxt->style->warnings++;
3327 findSpaceAttr = 0;
3328 xmlFree(value);
3332 attr = attr->next;
3333 } while (attr != NULL);
3336 * We'll descend into the children of element nodes only.
3338 if (cur->children != NULL) {
3339 cur = cur->children;
3340 continue;
3342 } else if ((cur->type == XML_TEXT_NODE) ||
3343 (cur->type == XML_CDATA_SECTION_NODE))
3346 * Merge adjacent text/CDATA-section-nodes
3347 * ---------------------------------------
3348 * In order to avoid breaking of existing stylesheets,
3349 * if the old behaviour is wanted (strictWhitespace == 0),
3350 * then we *won't* merge adjacent text-nodes
3351 * (except in xsl:text); this will ensure that whitespace-only
3352 * text nodes are (incorrectly) not stripped in some cases.
3354 * Example: : <foo> <!-- bar -->zoo</foo>
3355 * Corrent (strict) result: <foo> zoo</foo>
3356 * Incorrect (old) result : <foo>zoo</foo>
3358 * NOTE that we *will* merge adjacent text-nodes if
3359 * they are in xsl:text.
3360 * Example, the following:
3361 * <xsl:text> <!-- bar -->zoo<xsl:text>
3362 * will result in both cases in:
3363 * <xsl:text> zoo<xsl:text>
3365 cur->type = XML_TEXT_NODE;
3366 if ((strictWhitespace != 0) || (inXSLText != 0)) {
3368 * New behaviour; merge nodes.
3370 if (textNode == NULL)
3371 textNode = cur;
3372 else {
3373 if (cur->content != NULL)
3374 xmlNodeAddContent(textNode, cur->content);
3375 deleteNode = cur;
3377 if ((cur->next == NULL) ||
3378 (cur->next->type == XML_ELEMENT_NODE))
3379 goto end_of_text;
3380 else
3381 goto next_sibling;
3382 } else {
3384 * Old behaviour.
3386 if (textNode == NULL)
3387 textNode = cur;
3388 goto end_of_text;
3390 } else if ((cur->type == XML_COMMENT_NODE) ||
3391 (cur->type == XML_PI_NODE))
3394 * Remove processing instructions and comments.
3396 deleteNode = cur;
3397 if ((cur->next == NULL) ||
3398 (cur->next->type == XML_ELEMENT_NODE))
3399 goto end_of_text;
3400 else
3401 goto next_sibling;
3402 } else {
3403 textNode = NULL;
3405 * Invalid node-type for this data-model.
3407 xsltTransformError(NULL, style, cur,
3408 "Invalid type of node for the XSLT data model.\n");
3409 cctxt->style->errors++;
3410 goto next_sibling;
3413 end_of_text:
3414 if (textNode) {
3415 value = textNode->content;
3417 * At this point all adjacent text/CDATA-section nodes
3418 * have been merged.
3420 * Strip whitespace-only text-nodes.
3421 * (cctxt->inode->stripWhitespace)
3423 if ((value == NULL) || (*value == 0) ||
3424 (((cctxt->inode->stripWhitespace) ||
3425 (! cctxt->inode->preserveWhitespace)) &&
3426 IS_BLANK(*value) &&
3427 xsltIsBlank(value)))
3429 if (textNode != cur) {
3430 xmlUnlinkNode(textNode);
3431 xmlFreeNode(textNode);
3432 } else
3433 deleteNode = textNode;
3434 textNode = NULL;
3435 goto next_sibling;
3438 * Convert CDATA-section nodes to text-nodes.
3439 * TODO: Can this produce problems?
3441 if (textNode->type != XML_TEXT_NODE) {
3442 textNode->type = XML_TEXT_NODE;
3443 textNode->name = xmlStringText;
3445 if (internalize &&
3446 (textNode->content != NULL) &&
3447 (!xmlDictOwns(style->dict, textNode->content)))
3450 * Internalize the string.
3452 value = (xmlChar *) xmlDictLookup(style->dict,
3453 textNode->content, -1);
3454 xmlNodeSetContent(textNode, NULL);
3455 textNode->content = value;
3457 textNode = NULL;
3459 * Note that "disable-output-escaping" of the xsl:text
3460 * element will be applied at a later level, when
3461 * XSLT elements are processed.
3465 next_sibling:
3466 if (cur->type == XML_ELEMENT_NODE) {
3467 xsltCompilerNodePop(cctxt, cur);
3469 if (cur == node)
3470 break;
3471 if (cur->next != NULL) {
3472 cur = cur->next;
3473 } else {
3474 cur = cur->parent;
3475 inXSLText = 0;
3476 goto next_sibling;
3479 if (deleteNode != NULL) {
3480 #ifdef WITH_XSLT_DEBUG_PARSING
3481 xsltGenericDebug(xsltGenericDebugContext,
3482 "xsltParsePreprocessStylesheetTree: removing node\n");
3483 #endif
3484 xmlUnlinkNode(deleteNode);
3485 xmlFreeNode(deleteNode);
3487 return(0);
3489 internal_err:
3490 return(-1);
3493 #endif /* XSLT_REFACTORED */
3495 #ifdef XSLT_REFACTORED
3496 #else
3497 static void
3498 xsltPreprocessStylesheet(xsltStylesheetPtr style, xmlNodePtr cur)
3500 xmlNodePtr deleteNode, styleelem;
3501 int internalize = 0;
3503 if ((style == NULL) || (cur == NULL))
3504 return;
3506 if ((cur->doc != NULL) && (style->dict != NULL) &&
3507 (cur->doc->dict == style->dict))
3508 internalize = 1;
3509 else
3510 style->internalized = 0;
3512 if ((cur != NULL) && (IS_XSLT_ELEM(cur)) &&
3513 (IS_XSLT_NAME(cur, "stylesheet"))) {
3514 styleelem = cur;
3515 } else {
3516 styleelem = NULL;
3520 * This content comes from the stylesheet
3521 * For stylesheets, the set of whitespace-preserving
3522 * element names consists of just xsl:text.
3524 deleteNode = NULL;
3525 while (cur != NULL) {
3526 if (deleteNode != NULL) {
3527 #ifdef WITH_XSLT_DEBUG_BLANKS
3528 xsltGenericDebug(xsltGenericDebugContext,
3529 "xsltPreprocessStylesheet: removing ignorable blank node\n");
3530 #endif
3531 xmlUnlinkNode(deleteNode);
3532 xmlFreeNode(deleteNode);
3533 deleteNode = NULL;
3535 if (cur->type == XML_ELEMENT_NODE) {
3536 int exclPrefixes;
3538 * Internalize attributes values.
3540 if ((internalize) && (cur->properties != NULL)) {
3541 xmlAttrPtr attr = cur->properties;
3542 xmlNodePtr txt;
3544 while (attr != NULL) {
3545 txt = attr->children;
3546 if ((txt != NULL) && (txt->type == XML_TEXT_NODE) &&
3547 (txt->content != NULL) &&
3548 (!xmlDictOwns(style->dict, txt->content)))
3550 xmlChar *tmp;
3553 * internalize the text string, goal is to speed
3554 * up operations and minimize used space by compiled
3555 * stylesheets.
3557 tmp = (xmlChar *) xmlDictLookup(style->dict,
3558 txt->content, -1);
3559 if (tmp != txt->content) {
3560 xmlNodeSetContent(txt, NULL);
3561 txt->content = tmp;
3564 attr = attr->next;
3567 if (IS_XSLT_ELEM(cur)) {
3568 exclPrefixes = 0;
3569 if (IS_XSLT_NAME(cur, "text")) {
3570 for (;exclPrefixes > 0;exclPrefixes--)
3571 exclPrefixPop(style);
3572 goto skip_children;
3574 } else {
3575 exclPrefixes = xsltParseStylesheetExcludePrefix(style, cur, 0);
3578 if ((cur->nsDef != NULL) && (style->exclPrefixNr > 0)) {
3579 xmlNsPtr ns = cur->nsDef, prev = NULL, next;
3580 xmlNodePtr root = NULL;
3581 int i, moved;
3583 root = xmlDocGetRootElement(cur->doc);
3584 if ((root != NULL) && (root != cur)) {
3585 while (ns != NULL) {
3586 moved = 0;
3587 next = ns->next;
3588 for (i = 0;i < style->exclPrefixNr;i++) {
3589 if ((ns->prefix != NULL) &&
3590 (xmlStrEqual(ns->href,
3591 style->exclPrefixTab[i]))) {
3593 * Move the namespace definition on the root
3594 * element to avoid duplicating it without
3595 * loosing it.
3597 if (prev == NULL) {
3598 cur->nsDef = ns->next;
3599 } else {
3600 prev->next = ns->next;
3602 ns->next = root->nsDef;
3603 root->nsDef = ns;
3604 moved = 1;
3605 break;
3608 if (moved == 0)
3609 prev = ns;
3610 ns = next;
3615 * If we have prefixes locally, recurse and pop them up when
3616 * going back
3618 if (exclPrefixes > 0) {
3619 xsltPreprocessStylesheet(style, cur->children);
3620 for (;exclPrefixes > 0;exclPrefixes--)
3621 exclPrefixPop(style);
3622 goto skip_children;
3624 } else if (cur->type == XML_TEXT_NODE) {
3625 if (IS_BLANK_NODE(cur)) {
3626 if (xmlNodeGetSpacePreserve(cur->parent) != 1) {
3627 deleteNode = cur;
3629 } else if ((cur->content != NULL) && (internalize) &&
3630 (!xmlDictOwns(style->dict, cur->content))) {
3631 xmlChar *tmp;
3634 * internalize the text string, goal is to speed
3635 * up operations and minimize used space by compiled
3636 * stylesheets.
3638 tmp = (xmlChar *) xmlDictLookup(style->dict, cur->content, -1);
3639 xmlNodeSetContent(cur, NULL);
3640 cur->content = tmp;
3642 } else if ((cur->type != XML_ELEMENT_NODE) &&
3643 (cur->type != XML_CDATA_SECTION_NODE)) {
3644 deleteNode = cur;
3645 goto skip_children;
3649 * Skip to next node. In case of a namespaced element children of
3650 * the stylesheet and not in the XSLT namespace and not an extension
3651 * element, ignore its content.
3653 if ((cur->type == XML_ELEMENT_NODE) && (cur->ns != NULL) &&
3654 (styleelem != NULL) && (cur->parent == styleelem) &&
3655 (!xmlStrEqual(cur->ns->href, XSLT_NAMESPACE)) &&
3656 (!xsltCheckExtURI(style, cur->ns->href))) {
3657 goto skip_children;
3658 } else if (cur->children != NULL) {
3659 cur = cur->children;
3660 continue;
3663 skip_children:
3664 if (cur->next != NULL) {
3665 cur = cur->next;
3666 continue;
3668 do {
3670 cur = cur->parent;
3671 if (cur == NULL)
3672 break;
3673 if (cur == (xmlNodePtr) style->doc) {
3674 cur = NULL;
3675 break;
3677 if (cur->next != NULL) {
3678 cur = cur->next;
3679 break;
3681 } while (cur != NULL);
3683 if (deleteNode != NULL) {
3684 #ifdef WITH_XSLT_DEBUG_PARSING
3685 xsltGenericDebug(xsltGenericDebugContext,
3686 "xsltPreprocessStylesheet: removing ignorable blank node\n");
3687 #endif
3688 xmlUnlinkNode(deleteNode);
3689 xmlFreeNode(deleteNode);
3692 #endif /* end of else XSLT_REFACTORED */
3695 * xsltGatherNamespaces:
3696 * @style: the XSLT stylesheet
3698 * Browse the stylesheet and build the namspace hash table which
3699 * will be used for XPath interpretation. If needed do a bit of normalization
3702 static void
3703 xsltGatherNamespaces(xsltStylesheetPtr style) {
3704 xmlNodePtr cur;
3705 const xmlChar *URI;
3707 if (style == NULL)
3708 return;
3710 * TODO: basically if the stylesheet uses the same prefix for different
3711 * patterns, well they may be in problem, hopefully they will get
3712 * a warning first.
3715 * TODO: Eliminate the use of the hash for XPath expressions.
3716 * An expression should be evaluated in the context of the in-scope
3717 * namespaces; eliminate the restriction of an XML document to contain
3718 * no duplicate prefixes for different namespace names.
3721 cur = xmlDocGetRootElement(style->doc);
3722 while (cur != NULL) {
3723 if (cur->type == XML_ELEMENT_NODE) {
3724 xmlNsPtr ns = cur->nsDef;
3725 while (ns != NULL) {
3726 if (ns->prefix != NULL) {
3727 if (style->nsHash == NULL) {
3728 style->nsHash = xmlHashCreate(10);
3729 if (style->nsHash == NULL) {
3730 xsltTransformError(NULL, style, cur,
3731 "xsltGatherNamespaces: failed to create hash table\n");
3732 style->errors++;
3733 return;
3736 URI = xmlHashLookup(style->nsHash, ns->prefix);
3737 if ((URI != NULL) && (!xmlStrEqual(URI, ns->href))) {
3738 xsltTransformError(NULL, style, cur,
3739 "Namespaces prefix %s used for multiple namespaces\n",ns->prefix);
3740 style->warnings++;
3741 } else if (URI == NULL) {
3742 xmlHashUpdateEntry(style->nsHash, ns->prefix,
3743 (void *) ns->href, NULL);
3745 #ifdef WITH_XSLT_DEBUG_PARSING
3746 xsltGenericDebug(xsltGenericDebugContext,
3747 "Added namespace: %s mapped to %s\n", ns->prefix, ns->href);
3748 #endif
3751 ns = ns->next;
3756 * Skip to next node
3758 if (cur->children != NULL) {
3759 if (cur->children->type != XML_ENTITY_DECL) {
3760 cur = cur->children;
3761 continue;
3764 if (cur->next != NULL) {
3765 cur = cur->next;
3766 continue;
3769 do {
3770 cur = cur->parent;
3771 if (cur == NULL)
3772 break;
3773 if (cur == (xmlNodePtr) style->doc) {
3774 cur = NULL;
3775 break;
3777 if (cur->next != NULL) {
3778 cur = cur->next;
3779 break;
3781 } while (cur != NULL);
3785 #ifdef XSLT_REFACTORED
3787 static xsltStyleType
3788 xsltGetXSLTElementTypeByNode(xsltCompilerCtxtPtr cctxt,
3789 xmlNodePtr node)
3791 if ((node == NULL) || (node->type != XML_ELEMENT_NODE) ||
3792 (node->name == NULL))
3793 return(0);
3795 if (node->name[0] == 'a') {
3796 if (IS_XSLT_NAME(node, "apply-templates"))
3797 return(XSLT_FUNC_APPLYTEMPLATES);
3798 else if (IS_XSLT_NAME(node, "attribute"))
3799 return(XSLT_FUNC_ATTRIBUTE);
3800 else if (IS_XSLT_NAME(node, "apply-imports"))
3801 return(XSLT_FUNC_APPLYIMPORTS);
3802 else if (IS_XSLT_NAME(node, "attribute-set"))
3803 return(0);
3805 } else if (node->name[0] == 'c') {
3806 if (IS_XSLT_NAME(node, "choose"))
3807 return(XSLT_FUNC_CHOOSE);
3808 else if (IS_XSLT_NAME(node, "copy"))
3809 return(XSLT_FUNC_COPY);
3810 else if (IS_XSLT_NAME(node, "copy-of"))
3811 return(XSLT_FUNC_COPYOF);
3812 else if (IS_XSLT_NAME(node, "call-template"))
3813 return(XSLT_FUNC_CALLTEMPLATE);
3814 else if (IS_XSLT_NAME(node, "comment"))
3815 return(XSLT_FUNC_COMMENT);
3817 } else if (node->name[0] == 'd') {
3818 if (IS_XSLT_NAME(node, "document"))
3819 return(XSLT_FUNC_DOCUMENT);
3820 else if (IS_XSLT_NAME(node, "decimal-format"))
3821 return(0);
3823 } else if (node->name[0] == 'e') {
3824 if (IS_XSLT_NAME(node, "element"))
3825 return(XSLT_FUNC_ELEMENT);
3827 } else if (node->name[0] == 'f') {
3828 if (IS_XSLT_NAME(node, "for-each"))
3829 return(XSLT_FUNC_FOREACH);
3830 else if (IS_XSLT_NAME(node, "fallback"))
3831 return(XSLT_FUNC_FALLBACK);
3833 } else if (*(node->name) == 'i') {
3834 if (IS_XSLT_NAME(node, "if"))
3835 return(XSLT_FUNC_IF);
3836 else if (IS_XSLT_NAME(node, "include"))
3837 return(0);
3838 else if (IS_XSLT_NAME(node, "import"))
3839 return(0);
3841 } else if (*(node->name) == 'k') {
3842 if (IS_XSLT_NAME(node, "key"))
3843 return(0);
3845 } else if (*(node->name) == 'm') {
3846 if (IS_XSLT_NAME(node, "message"))
3847 return(XSLT_FUNC_MESSAGE);
3849 } else if (*(node->name) == 'n') {
3850 if (IS_XSLT_NAME(node, "number"))
3851 return(XSLT_FUNC_NUMBER);
3852 else if (IS_XSLT_NAME(node, "namespace-alias"))
3853 return(0);
3855 } else if (*(node->name) == 'o') {
3856 if (IS_XSLT_NAME(node, "otherwise"))
3857 return(XSLT_FUNC_OTHERWISE);
3858 else if (IS_XSLT_NAME(node, "output"))
3859 return(0);
3861 } else if (*(node->name) == 'p') {
3862 if (IS_XSLT_NAME(node, "param"))
3863 return(XSLT_FUNC_PARAM);
3864 else if (IS_XSLT_NAME(node, "processing-instruction"))
3865 return(XSLT_FUNC_PI);
3866 else if (IS_XSLT_NAME(node, "preserve-space"))
3867 return(0);
3869 } else if (*(node->name) == 's') {
3870 if (IS_XSLT_NAME(node, "sort"))
3871 return(XSLT_FUNC_SORT);
3872 else if (IS_XSLT_NAME(node, "strip-space"))
3873 return(0);
3874 else if (IS_XSLT_NAME(node, "stylesheet"))
3875 return(0);
3877 } else if (node->name[0] == 't') {
3878 if (IS_XSLT_NAME(node, "text"))
3879 return(XSLT_FUNC_TEXT);
3880 else if (IS_XSLT_NAME(node, "template"))
3881 return(0);
3882 else if (IS_XSLT_NAME(node, "transform"))
3883 return(0);
3885 } else if (*(node->name) == 'v') {
3886 if (IS_XSLT_NAME(node, "value-of"))
3887 return(XSLT_FUNC_VALUEOF);
3888 else if (IS_XSLT_NAME(node, "variable"))
3889 return(XSLT_FUNC_VARIABLE);
3891 } else if (*(node->name) == 'w') {
3892 if (IS_XSLT_NAME(node, "when"))
3893 return(XSLT_FUNC_WHEN);
3894 if (IS_XSLT_NAME(node, "with-param"))
3895 return(XSLT_FUNC_WITHPARAM);
3897 return(0);
3901 * xsltParseAnyXSLTElem:
3903 * @cctxt: the compilation context
3904 * @elem: the element node of the XSLT instruction
3906 * Parses, validates the content models and compiles XSLT instructions.
3908 * Returns 0 if everything's fine;
3909 * -1 on API or internal errors.
3912 xsltParseAnyXSLTElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr elem)
3914 if ((cctxt == NULL) || (elem == NULL) ||
3915 (elem->type != XML_ELEMENT_NODE))
3916 return(-1);
3918 elem->psvi = NULL;
3920 if (! (IS_XSLT_ELEM_FAST(elem)))
3921 return(-1);
3923 * Detection of handled content of extension instructions.
3925 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
3926 cctxt->inode->extContentHandled = 1;
3929 xsltCompilerNodePush(cctxt, elem);
3931 * URGENT TODO: Find a way to speed up this annoying redundant
3932 * textual node-name and namespace comparison.
3934 if (cctxt->inode->prev->curChildType != 0)
3935 cctxt->inode->type = cctxt->inode->prev->curChildType;
3936 else
3937 cctxt->inode->type = xsltGetXSLTElementTypeByNode(cctxt, elem);
3939 * Update the in-scope namespaces if needed.
3941 if (elem->nsDef != NULL)
3942 cctxt->inode->inScopeNs =
3943 xsltCompilerBuildInScopeNsList(cctxt, elem);
3945 * xsltStylePreCompute():
3946 * This will compile the information found on the current
3947 * element's attributes. NOTE that this won't process the
3948 * children of the instruction.
3950 xsltStylePreCompute(cctxt->style, elem);
3952 * TODO: How to react on errors in xsltStylePreCompute() ?
3956 * Validate the content model of the XSLT-element.
3958 switch (cctxt->inode->type) {
3959 case XSLT_FUNC_APPLYIMPORTS:
3960 /* EMPTY */
3961 goto empty_content;
3962 case XSLT_FUNC_APPLYTEMPLATES:
3963 /* <!-- Content: (xsl:sort | xsl:with-param)* --> */
3964 goto apply_templates;
3965 case XSLT_FUNC_ATTRIBUTE:
3966 /* <!-- Content: template --> */
3967 goto sequence_constructor;
3968 case XSLT_FUNC_CALLTEMPLATE:
3969 /* <!-- Content: xsl:with-param* --> */
3970 goto call_template;
3971 case XSLT_FUNC_CHOOSE:
3972 /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */
3973 goto choose;
3974 case XSLT_FUNC_COMMENT:
3975 /* <!-- Content: template --> */
3976 goto sequence_constructor;
3977 case XSLT_FUNC_COPY:
3978 /* <!-- Content: template --> */
3979 goto sequence_constructor;
3980 case XSLT_FUNC_COPYOF:
3981 /* EMPTY */
3982 goto empty_content;
3983 case XSLT_FUNC_DOCUMENT: /* Extra one */
3984 /* ?? template ?? */
3985 goto sequence_constructor;
3986 case XSLT_FUNC_ELEMENT:
3987 /* <!-- Content: template --> */
3988 goto sequence_constructor;
3989 case XSLT_FUNC_FALLBACK:
3990 /* <!-- Content: template --> */
3991 goto sequence_constructor;
3992 case XSLT_FUNC_FOREACH:
3993 /* <!-- Content: (xsl:sort*, template) --> */
3994 goto for_each;
3995 case XSLT_FUNC_IF:
3996 /* <!-- Content: template --> */
3997 goto sequence_constructor;
3998 case XSLT_FUNC_OTHERWISE:
3999 /* <!-- Content: template --> */
4000 goto sequence_constructor;
4001 case XSLT_FUNC_MESSAGE:
4002 /* <!-- Content: template --> */
4003 goto sequence_constructor;
4004 case XSLT_FUNC_NUMBER:
4005 /* EMPTY */
4006 goto empty_content;
4007 case XSLT_FUNC_PARAM:
4009 * Check for redefinition.
4011 if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
4012 xsltVarInfoPtr ivar = cctxt->ivar;
4014 do {
4015 if ((ivar->name ==
4016 ((xsltStyleItemParamPtr) elem->psvi)->name) &&
4017 (ivar->nsName ==
4018 ((xsltStyleItemParamPtr) elem->psvi)->ns))
4020 elem->psvi = NULL;
4021 xsltTransformError(NULL, cctxt->style, elem,
4022 "Redefinition of variable or parameter '%s'.\n",
4023 ivar->name);
4024 cctxt->style->errors++;
4025 goto error;
4027 ivar = ivar->prev;
4028 } while (ivar != NULL);
4030 /* <!-- Content: template --> */
4031 goto sequence_constructor;
4032 case XSLT_FUNC_PI:
4033 /* <!-- Content: template --> */
4034 goto sequence_constructor;
4035 case XSLT_FUNC_SORT:
4036 /* EMPTY */
4037 goto empty_content;
4038 case XSLT_FUNC_TEXT:
4039 /* <!-- Content: #PCDATA --> */
4040 goto text;
4041 case XSLT_FUNC_VALUEOF:
4042 /* EMPTY */
4043 goto empty_content;
4044 case XSLT_FUNC_VARIABLE:
4046 * Check for redefinition.
4048 if ((elem->psvi != NULL) && (cctxt->ivar != NULL)) {
4049 xsltVarInfoPtr ivar = cctxt->ivar;
4051 do {
4052 if ((ivar->name ==
4053 ((xsltStyleItemVariablePtr) elem->psvi)->name) &&
4054 (ivar->nsName ==
4055 ((xsltStyleItemVariablePtr) elem->psvi)->ns))
4057 elem->psvi = NULL;
4058 xsltTransformError(NULL, cctxt->style, elem,
4059 "Redefinition of variable or parameter '%s'.\n",
4060 ivar->name);
4061 cctxt->style->errors++;
4062 goto error;
4064 ivar = ivar->prev;
4065 } while (ivar != NULL);
4067 /* <!-- Content: template --> */
4068 goto sequence_constructor;
4069 case XSLT_FUNC_WHEN:
4070 /* <!-- Content: template --> */
4071 goto sequence_constructor;
4072 case XSLT_FUNC_WITHPARAM:
4073 /* <!-- Content: template --> */
4074 goto sequence_constructor;
4075 default:
4076 #ifdef WITH_XSLT_DEBUG_PARSING
4077 xsltGenericDebug(xsltGenericDebugContext,
4078 "xsltParseXSLTNode: Unhandled XSLT element '%s'.\n",
4079 elem->name);
4080 #endif
4081 xsltTransformError(NULL, cctxt->style, elem,
4082 "xsltParseXSLTNode: Internal error; "
4083 "unhandled XSLT element '%s'.\n", elem->name);
4084 cctxt->style->errors++;
4085 goto internal_err;
4088 apply_templates:
4089 /* <!-- Content: (xsl:sort | xsl:with-param)* --> */
4090 if (elem->children != NULL) {
4091 xmlNodePtr child = elem->children;
4092 do {
4093 if (child->type == XML_ELEMENT_NODE) {
4094 if (IS_XSLT_ELEM_FAST(child)) {
4095 if (xmlStrEqual(child->name, BAD_CAST "with-param")) {
4096 cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM;
4097 xsltParseAnyXSLTElem(cctxt, child);
4098 } else if (xmlStrEqual(child->name, BAD_CAST "sort")) {
4099 cctxt->inode->curChildType = XSLT_FUNC_SORT;
4100 xsltParseAnyXSLTElem(cctxt, child);
4101 } else
4102 xsltParseContentError(cctxt->style, child);
4103 } else
4104 xsltParseContentError(cctxt->style, child);
4106 child = child->next;
4107 } while (child != NULL);
4109 goto exit;
4111 call_template:
4112 /* <!-- Content: xsl:with-param* --> */
4113 if (elem->children != NULL) {
4114 xmlNodePtr child = elem->children;
4115 do {
4116 if (child->type == XML_ELEMENT_NODE) {
4117 if (IS_XSLT_ELEM_FAST(child)) {
4118 xsltStyleType type;
4120 type = xsltGetXSLTElementTypeByNode(cctxt, child);
4121 if (type == XSLT_FUNC_WITHPARAM) {
4122 cctxt->inode->curChildType = XSLT_FUNC_WITHPARAM;
4123 xsltParseAnyXSLTElem(cctxt, child);
4124 } else {
4125 xsltParseContentError(cctxt->style, child);
4127 } else
4128 xsltParseContentError(cctxt->style, child);
4130 child = child->next;
4131 } while (child != NULL);
4133 goto exit;
4135 text:
4136 if (elem->children != NULL) {
4137 xmlNodePtr child = elem->children;
4138 do {
4139 if ((child->type != XML_TEXT_NODE) &&
4140 (child->type != XML_CDATA_SECTION_NODE))
4142 xsltTransformError(NULL, cctxt->style, elem,
4143 "The XSLT 'text' element must have only character "
4144 "data as content.\n");
4146 child = child->next;
4147 } while (child != NULL);
4149 goto exit;
4151 empty_content:
4152 if (elem->children != NULL) {
4153 xmlNodePtr child = elem->children;
4155 * Relaxed behaviour: we will allow whitespace-only text-nodes.
4157 do {
4158 if (((child->type != XML_TEXT_NODE) &&
4159 (child->type != XML_CDATA_SECTION_NODE)) ||
4160 (! IS_BLANK_NODE(child)))
4162 xsltTransformError(NULL, cctxt->style, elem,
4163 "This XSLT element must have no content.\n");
4164 cctxt->style->errors++;
4165 break;
4167 child = child->next;
4168 } while (child != NULL);
4170 goto exit;
4172 choose:
4173 /* <!-- Content: (xsl:when+, xsl:otherwise?) --> */
4175 * TODO: text-nodes in between are *not* allowed in XSLT 1.0.
4176 * The old behaviour did not check this.
4177 * NOTE: In XSLT 2.0 they are stripped beforehand
4178 * if whitespace-only (regardless of xml:space).
4180 if (elem->children != NULL) {
4181 xmlNodePtr child = elem->children;
4182 int nbWhen = 0, nbOtherwise = 0, err = 0;
4183 do {
4184 if (child->type == XML_ELEMENT_NODE) {
4185 if (IS_XSLT_ELEM_FAST(child)) {
4186 xsltStyleType type;
4188 type = xsltGetXSLTElementTypeByNode(cctxt, child);
4189 if (type == XSLT_FUNC_WHEN) {
4190 nbWhen++;
4191 if (nbOtherwise) {
4192 xsltParseContentError(cctxt->style, child);
4193 err = 1;
4194 break;
4196 cctxt->inode->curChildType = XSLT_FUNC_WHEN;
4197 xsltParseAnyXSLTElem(cctxt, child);
4198 } else if (type == XSLT_FUNC_OTHERWISE) {
4199 if (! nbWhen) {
4200 xsltParseContentError(cctxt->style, child);
4201 err = 1;
4202 break;
4204 if (nbOtherwise) {
4205 xsltTransformError(NULL, cctxt->style, elem,
4206 "The XSLT 'choose' element must not contain "
4207 "more than one XSLT 'otherwise' element.\n");
4208 cctxt->style->errors++;
4209 err = 1;
4210 break;
4212 nbOtherwise++;
4213 cctxt->inode->curChildType = XSLT_FUNC_OTHERWISE;
4214 xsltParseAnyXSLTElem(cctxt, child);
4215 } else
4216 xsltParseContentError(cctxt->style, child);
4217 } else
4218 xsltParseContentError(cctxt->style, child);
4221 else
4222 xsltParseContentError(cctxt, child);
4224 child = child->next;
4225 } while (child != NULL);
4226 if ((! err) && (! nbWhen)) {
4227 xsltTransformError(NULL, cctxt->style, elem,
4228 "The XSLT element 'choose' must contain at least one "
4229 "XSLT element 'when'.\n");
4230 cctxt->style->errors++;
4233 goto exit;
4235 for_each:
4236 /* <!-- Content: (xsl:sort*, template) --> */
4238 * NOTE: Text-nodes before xsl:sort are *not* allowed in XSLT 1.0.
4239 * The old behaviour did not allow this, but it catched this
4240 * only at transformation-time.
4241 * In XSLT 2.0 they are stripped beforehand if whitespace-only
4242 * (regardless of xml:space).
4244 if (elem->children != NULL) {
4245 xmlNodePtr child = elem->children;
4247 * Parse xsl:sort first.
4249 do {
4250 if ((child->type == XML_ELEMENT_NODE) &&
4251 IS_XSLT_ELEM_FAST(child))
4253 if (xsltGetXSLTElementTypeByNode(cctxt, child) ==
4254 XSLT_FUNC_SORT)
4256 cctxt->inode->curChildType = XSLT_FUNC_SORT;
4257 xsltParseAnyXSLTElem(cctxt, child);
4258 } else
4259 break;
4260 } else
4261 break;
4262 child = child->next;
4263 } while (child != NULL);
4265 * Parse the sequece constructor.
4267 if (child != NULL)
4268 xsltParseSequenceConstructor(cctxt, child);
4270 goto exit;
4272 sequence_constructor:
4274 * Parse the sequence constructor.
4276 if (elem->children != NULL)
4277 xsltParseSequenceConstructor(cctxt, elem->children);
4280 * Register information for vars/params. Only needed if there
4281 * are any following siblings.
4283 if ((elem->next != NULL) &&
4284 ((cctxt->inode->type == XSLT_FUNC_VARIABLE) ||
4285 (cctxt->inode->type == XSLT_FUNC_PARAM)))
4287 if ((elem->psvi != NULL) &&
4288 (((xsltStyleBasicItemVariablePtr) elem->psvi)->name))
4290 xsltCompilerVarInfoPush(cctxt, elem,
4291 ((xsltStyleBasicItemVariablePtr) elem->psvi)->name,
4292 ((xsltStyleBasicItemVariablePtr) elem->psvi)->ns);
4296 error:
4297 exit:
4298 xsltCompilerNodePop(cctxt, elem);
4299 return(0);
4301 internal_err:
4302 xsltCompilerNodePop(cctxt, elem);
4303 return(-1);
4307 * xsltForwardsCompatUnkownItemCreate:
4309 * @cctxt: the compilation context
4311 * Creates a compiled representation of the unknown
4312 * XSLT instruction.
4314 * Returns the compiled representation.
4316 static xsltStyleItemUknownPtr
4317 xsltForwardsCompatUnkownItemCreate(xsltCompilerCtxtPtr cctxt)
4319 xsltStyleItemUknownPtr item;
4321 item = (xsltStyleItemUknownPtr) xmlMalloc(sizeof(xsltStyleItemUknown));
4322 if (item == NULL) {
4323 xsltTransformError(NULL, cctxt->style, NULL,
4324 "Internal error in xsltForwardsCompatUnkownItemCreate(): "
4325 "Failed to allocate memory.\n");
4326 cctxt->style->errors++;
4327 return(NULL);
4329 memset(item, 0, sizeof(xsltStyleItemUknown));
4330 item->type = XSLT_FUNC_UNKOWN_FORWARDS_COMPAT;
4332 * Store it in the stylesheet.
4334 item->next = cctxt->style->preComps;
4335 cctxt->style->preComps = (xsltElemPreCompPtr) item;
4336 return(item);
4340 * xsltParseUnknownXSLTElem:
4342 * @cctxt: the compilation context
4343 * @node: the element of the unknown XSLT instruction
4345 * Parses an unknown XSLT element.
4346 * If forwards compatible mode is enabled this will allow
4347 * such an unknown XSLT and; otherwise it is rejected.
4349 * Returns 1 in the unknown XSLT instruction is rejected,
4350 * 0 if everything's fine and
4351 * -1 on API or internal errors.
4353 static int
4354 xsltParseUnknownXSLTElem(xsltCompilerCtxtPtr cctxt,
4355 xmlNodePtr node)
4357 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
4358 return(-1);
4361 * Detection of handled content of extension instructions.
4363 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
4364 cctxt->inode->extContentHandled = 1;
4366 if (cctxt->inode->forwardsCompat == 0) {
4368 * We are not in forwards-compatible mode, so raise an error.
4370 xsltTransformError(NULL, cctxt->style, node,
4371 "Unknown XSLT element '%s'.\n", node->name);
4372 cctxt->style->errors++;
4373 return(1);
4376 * Forwards-compatible mode.
4377 * ------------------------
4379 * Parse/compile xsl:fallback elements.
4381 * QUESTION: Do we have to raise an error if there's no xsl:fallback?
4382 * ANSWER: No, since in the stylesheet the fallback behaviour might
4383 * also be provided by using the XSLT function "element-available".
4385 if (cctxt->unknownItem == NULL) {
4387 * Create a singleton for all unknown XSLT instructions.
4389 cctxt->unknownItem = xsltForwardsCompatUnkownItemCreate(cctxt);
4390 if (cctxt->unknownItem == NULL) {
4391 node->psvi = NULL;
4392 return(-1);
4395 node->psvi = cctxt->unknownItem;
4396 if (node->children == NULL)
4397 return(0);
4398 else {
4399 xmlNodePtr child = node->children;
4401 xsltCompilerNodePush(cctxt, node);
4403 * Update the in-scope namespaces if needed.
4405 if (node->nsDef != NULL)
4406 cctxt->inode->inScopeNs =
4407 xsltCompilerBuildInScopeNsList(cctxt, node);
4409 * Parse all xsl:fallback children.
4411 do {
4412 if ((child->type == XML_ELEMENT_NODE) &&
4413 IS_XSLT_ELEM_FAST(child) &&
4414 IS_XSLT_NAME(child, "fallback"))
4416 cctxt->inode->curChildType = XSLT_FUNC_FALLBACK;
4417 xsltParseAnyXSLTElem(cctxt, child);
4419 child = child->next;
4420 } while (child != NULL);
4422 xsltCompilerNodePop(cctxt, node);
4424 return(0);
4428 * xsltParseSequenceConstructor:
4430 * @cctxt: the compilation context
4431 * @cur: the start-node of the content to be parsed
4433 * Parses a "template" content (or "sequence constructor" in XSLT 2.0 terms).
4434 * This will additionally remove xsl:text elements from the tree.
4436 void
4437 xsltParseSequenceConstructor(xsltCompilerCtxtPtr cctxt, xmlNodePtr cur)
4439 xsltStyleType type;
4440 xmlNodePtr deleteNode = NULL;
4442 if (cctxt == NULL) {
4443 xmlGenericError(xmlGenericErrorContext,
4444 "xsltParseSequenceConstructor: Bad arguments\n");
4445 cctxt->style->errors++;
4446 return;
4449 * Detection of handled content of extension instructions.
4451 if (cctxt->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
4452 cctxt->inode->extContentHandled = 1;
4454 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL))
4455 return;
4457 * This is the content reffered to as a "template".
4458 * E.g. an xsl:element has such content model:
4459 * <xsl:element
4460 * name = { qname }
4461 * namespace = { uri-reference }
4462 * use-attribute-sets = qnames>
4463 * <!-- Content: template -->
4465 * NOTE that in XSLT-2 the term "template" was abandoned due to
4466 * confusion with xsl:template and the term "sequence constructor"
4467 * was introduced instead.
4469 * The following XSLT-instructions are allowed to appear:
4470 * xsl:apply-templates, xsl:call-template, xsl:apply-imports,
4471 * xsl:for-each, xsl:value-of, xsl:copy-of, xsl:number,
4472 * xsl:choose, xsl:if, xsl:text, xsl:copy, xsl:variable,
4473 * xsl:message, xsl:fallback,
4474 * xsl:processing-instruction, xsl:comment, xsl:element
4475 * xsl:attribute.
4476 * Additional allowed content:
4477 * 1) extension instructions
4478 * 2) literal result elements
4479 * 3) PCDATA
4481 * NOTE that this content model does *not* allow xsl:param.
4483 while (cur != NULL) {
4484 if (deleteNode != NULL) {
4485 #ifdef WITH_XSLT_DEBUG_BLANKS
4486 xsltGenericDebug(xsltGenericDebugContext,
4487 "xsltParseSequenceConstructor: removing xsl:text element\n");
4488 #endif
4489 xmlUnlinkNode(deleteNode);
4490 xmlFreeNode(deleteNode);
4491 deleteNode = NULL;
4493 if (cur->type == XML_ELEMENT_NODE) {
4495 if (cur->psvi == xsltXSLTTextMarker) {
4497 * xsl:text elements
4498 * --------------------------------------------------------
4500 xmlNodePtr tmp;
4502 cur->psvi = NULL;
4504 * Mark the xsl:text element for later deletion.
4506 deleteNode = cur;
4508 * Validate content.
4510 tmp = cur->children;
4511 if (tmp) {
4513 * We don't expect more than one text-node in the
4514 * content, since we already merged adjacent
4515 * text/CDATA-nodes and eliminated PI/comment-nodes.
4517 if ((tmp->type == XML_TEXT_NODE) ||
4518 (tmp->next == NULL))
4521 * Leave the contained text-node in the tree.
4523 xmlUnlinkNode(tmp);
4524 xmlAddPrevSibling(cur, tmp);
4525 } else {
4526 tmp = NULL;
4527 xsltTransformError(NULL, cctxt->style, cur,
4528 "Element 'xsl:text': Invalid type "
4529 "of node found in content.\n");
4530 cctxt->style->errors++;
4533 if (cur->properties) {
4534 xmlAttrPtr attr;
4536 * TODO: We need to report errors for
4537 * invalid attrs.
4539 attr = cur->properties;
4540 do {
4541 if ((attr->ns == NULL) &&
4542 (attr->name != NULL) &&
4543 (attr->name[0] == 'd') &&
4544 xmlStrEqual(attr->name,
4545 BAD_CAST "disable-output-escaping"))
4548 * Attr "disable-output-escaping".
4549 * XSLT-2: This attribute is deprecated.
4551 if ((attr->children != NULL) &&
4552 xmlStrEqual(attr->children->content,
4553 BAD_CAST "yes"))
4556 * Disable output escaping for this
4557 * text node.
4559 if (tmp)
4560 tmp->name = xmlStringTextNoenc;
4561 } else if ((attr->children == NULL) ||
4562 (attr->children->content == NULL) ||
4563 (!xmlStrEqual(attr->children->content,
4564 BAD_CAST "no")))
4566 xsltTransformError(NULL, cctxt->style,
4567 cur,
4568 "Attribute 'disable-output-escaping': "
4569 "Invalid value. Expected is "
4570 "'yes' or 'no'.\n");
4571 cctxt->style->errors++;
4573 break;
4575 attr = attr->next;
4576 } while (attr != NULL);
4578 } else if (IS_XSLT_ELEM_FAST(cur)) {
4580 * TODO: Using the XSLT-marker is still not stable yet.
4582 /* if (cur->psvi == xsltXSLTElemMarker) { */
4584 * XSLT instructions
4585 * --------------------------------------------------------
4587 cur->psvi = NULL;
4588 type = xsltGetXSLTElementTypeByNode(cctxt, cur);
4589 switch (type) {
4590 case XSLT_FUNC_APPLYIMPORTS:
4591 case XSLT_FUNC_APPLYTEMPLATES:
4592 case XSLT_FUNC_ATTRIBUTE:
4593 case XSLT_FUNC_CALLTEMPLATE:
4594 case XSLT_FUNC_CHOOSE:
4595 case XSLT_FUNC_COMMENT:
4596 case XSLT_FUNC_COPY:
4597 case XSLT_FUNC_COPYOF:
4598 case XSLT_FUNC_DOCUMENT: /* Extra one */
4599 case XSLT_FUNC_ELEMENT:
4600 case XSLT_FUNC_FALLBACK:
4601 case XSLT_FUNC_FOREACH:
4602 case XSLT_FUNC_IF:
4603 case XSLT_FUNC_MESSAGE:
4604 case XSLT_FUNC_NUMBER:
4605 case XSLT_FUNC_PI:
4606 case XSLT_FUNC_TEXT:
4607 case XSLT_FUNC_VALUEOF:
4608 case XSLT_FUNC_VARIABLE:
4610 * Parse the XSLT element.
4612 cctxt->inode->curChildType = type;
4613 xsltParseAnyXSLTElem(cctxt, cur);
4614 break;
4615 default:
4616 xsltParseUnknownXSLTElem(cctxt, cur);
4617 cur = cur->next;
4618 continue;
4620 } else {
4622 * Non-XSLT elements
4623 * -----------------
4625 xsltCompilerNodePush(cctxt, cur);
4627 * Update the in-scope namespaces if needed.
4629 if (cur->nsDef != NULL)
4630 cctxt->inode->inScopeNs =
4631 xsltCompilerBuildInScopeNsList(cctxt, cur);
4633 * The current element is either a literal result element
4634 * or an extension instruction.
4636 * Process attr "xsl:extension-element-prefixes".
4637 * FUTURE TODO: IIRC in XSLT 2.0 this attribute must be
4638 * processed by the implementor of the extension function;
4639 * i.e., it won't be handled by the XSLT processor.
4641 /* SPEC 1.0:
4642 * "exclude-result-prefixes" is only allowed on literal
4643 * result elements and "xsl:exclude-result-prefixes"
4644 * on xsl:stylesheet/xsl:transform.
4645 * SPEC 2.0:
4646 * "There are a number of standard attributes
4647 * that may appear on any XSLT element: specifically
4648 * version, exclude-result-prefixes,
4649 * extension-element-prefixes, xpath-default-namespace,
4650 * default-collation, and use-when."
4652 * SPEC 2.0:
4653 * For literal result elements:
4654 * "xsl:version, xsl:exclude-result-prefixes,
4655 * xsl:extension-element-prefixes,
4656 * xsl:xpath-default-namespace,
4657 * xsl:default-collation, or xsl:use-when."
4659 if (cur->properties)
4660 cctxt->inode->extElemNs =
4661 xsltParseExtElemPrefixes(cctxt,
4662 cur, cctxt->inode->extElemNs,
4663 XSLT_ELEMENT_CATEGORY_LRE);
4665 * Eval if we have an extension instruction here.
4667 if ((cur->ns != NULL) &&
4668 (cctxt->inode->extElemNs != NULL) &&
4669 (xsltCheckExtPrefix(cctxt->style, cur->ns->href) == 1))
4672 * Extension instructions
4673 * ----------------------------------------------------
4674 * Mark the node information.
4676 cctxt->inode->category = XSLT_ELEMENT_CATEGORY_EXTENSION;
4677 cctxt->inode->extContentHandled = 0;
4678 if (cur->psvi != NULL) {
4679 cur->psvi = NULL;
4681 * TODO: Temporary sanity check.
4683 xsltTransformError(NULL, cctxt->style, cur,
4684 "Internal error in xsltParseSequenceConstructor(): "
4685 "Occupied PSVI field.\n");
4686 cctxt->style->errors++;
4687 cur = cur->next;
4688 continue;
4690 cur->psvi = (void *)
4691 xsltPreComputeExtModuleElement(cctxt->style, cur);
4693 if (cur->psvi == NULL) {
4695 * OLD COMMENT: "Unknown element, maybe registered
4696 * at the context level. Mark it for later
4697 * recognition."
4698 * QUESTION: What does the xsltExtMarker mean?
4699 * ANSWER: It is used in
4700 * xsltApplySequenceConstructor() at
4701 * transformation-time to look out for extension
4702 * registered in the transformation context.
4704 cur->psvi = (void *) xsltExtMarker;
4707 * BIG NOTE: Now the ugly part. In previous versions
4708 * of Libxslt (until 1.1.16), all the content of an
4709 * extension instruction was processed and compiled without
4710 * the need of the extension-author to explicitely call
4711 * such a processing;.We now need to mimic this old
4712 * behaviour in order to avoid breaking old code
4713 * on the extension-author's side.
4714 * The mechanism:
4715 * 1) If the author does *not* set the
4716 * compile-time-flag @extContentHandled, then we'll
4717 * parse the content assuming that it's a "template"
4718 * (or "sequence constructor in XSLT 2.0 terms).
4719 * NOTE: If the extension is registered at
4720 * transformation-time only, then there's no way of
4721 * knowing that content shall be valid, and we'll
4722 * process the content the same way.
4723 * 2) If the author *does* set the flag, then we'll assume
4724 * that the author has handled the parsing him/herself
4725 * (e.g. called xsltParseSequenceConstructor(), etc.
4726 * explicitely in his/her code).
4728 if ((cur->children != NULL) &&
4729 (cctxt->inode->extContentHandled == 0))
4732 * Default parsing of the content using the
4733 * sequence-constructor model.
4735 xsltParseSequenceConstructor(cctxt, cur->children);
4737 } else {
4739 * Literal result element
4740 * ----------------------------------------------------
4741 * Allowed XSLT attributes:
4742 * xsl:extension-element-prefixes CDATA #IMPLIED
4743 * xsl:exclude-result-prefixes CDATA #IMPLIED
4744 * TODO: xsl:use-attribute-sets %qnames; #IMPLIED
4745 * xsl:version NMTOKEN #IMPLIED
4747 cur->psvi = NULL;
4748 cctxt->inode->category = XSLT_ELEMENT_CATEGORY_LRE;
4749 if (cur->properties != NULL) {
4750 xmlAttrPtr attr = cur->properties;
4752 * Attribute "xsl:exclude-result-prefixes".
4754 cctxt->inode->exclResultNs =
4755 xsltParseExclResultPrefixes(cctxt, cur,
4756 cctxt->inode->exclResultNs,
4757 XSLT_ELEMENT_CATEGORY_LRE);
4759 * Attribute "xsl:version".
4761 xsltParseAttrXSLTVersion(cctxt, cur,
4762 XSLT_ELEMENT_CATEGORY_LRE);
4764 * Report invalid XSLT attributes.
4765 * For XSLT 1.0 only xsl:use-attribute-sets is allowed
4766 * next to xsl:version, xsl:exclude-result-prefixes and
4767 * xsl:extension-element-prefixes.
4769 * Mark all XSLT attributes, in order to skip such
4770 * attributes when instantiating the LRE.
4772 do {
4773 if ((attr->psvi != xsltXSLTAttrMarker) &&
4774 IS_XSLT_ATTR_FAST(attr))
4776 if (! xmlStrEqual(attr->name,
4777 BAD_CAST "use-attribute-sets"))
4779 xsltTransformError(NULL, cctxt->style,
4780 cur,
4781 "Unknown XSLT attribute '%s'.\n",
4782 attr->name);
4783 cctxt->style->errors++;
4784 } else {
4786 * XSLT attr marker.
4788 attr->psvi = (void *) xsltXSLTAttrMarker;
4791 attr = attr->next;
4792 } while (attr != NULL);
4795 * Create/reuse info for the literal result element.
4797 if (cctxt->inode->nsChanged)
4798 xsltLREInfoCreate(cctxt, cur, 1);
4799 cur->psvi = cctxt->inode->litResElemInfo;
4801 * Apply ns-aliasing on the element and on its attributes.
4803 if (cctxt->hasNsAliases)
4804 xsltLREBuildEffectiveNs(cctxt, cur);
4806 * Compile attribute value templates (AVT).
4808 if (cur->properties) {
4809 xmlAttrPtr attr = cur->properties;
4811 while (attr != NULL) {
4812 xsltCompileAttr(cctxt->style, attr);
4813 attr = attr->next;
4817 * Parse the content, which is defined to be a "template"
4818 * (or "sequence constructor" in XSLT 2.0 terms).
4820 if (cur->children != NULL) {
4821 xsltParseSequenceConstructor(cctxt, cur->children);
4825 * Leave the non-XSLT element.
4827 xsltCompilerNodePop(cctxt, cur);
4830 cur = cur->next;
4832 if (deleteNode != NULL) {
4833 #ifdef WITH_XSLT_DEBUG_BLANKS
4834 xsltGenericDebug(xsltGenericDebugContext,
4835 "xsltParseSequenceConstructor: removing xsl:text element\n");
4836 #endif
4837 xmlUnlinkNode(deleteNode);
4838 xmlFreeNode(deleteNode);
4839 deleteNode = NULL;
4844 * xsltParseTemplateContent:
4845 * @style: the XSLT stylesheet
4846 * @templ: the node containing the content to be parsed
4848 * Parses and compiles the content-model of an xsl:template element.
4849 * Note that this is *not* the "template" content model (or "sequence
4850 * constructor" in XSLT 2.0); it it allows addional xsl:param
4851 * elements as immediate children of @templ.
4853 * Called by:
4854 * exsltFuncFunctionComp() (EXSLT, functions.c)
4855 * So this is intended to be called from extension functions.
4857 void
4858 xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) {
4859 if ((style == NULL) || (templ == NULL) ||
4860 (templ->type == XML_NAMESPACE_DECL))
4861 return;
4864 * Detection of handled content of extension instructions.
4866 if (XSLT_CCTXT(style)->inode->category == XSLT_ELEMENT_CATEGORY_EXTENSION) {
4867 XSLT_CCTXT(style)->inode->extContentHandled = 1;
4870 if (templ->children != NULL) {
4871 xmlNodePtr child = templ->children;
4873 * Process xsl:param elements, which can only occur as the
4874 * immediate children of xsl:template (well, and of any
4875 * user-defined extension instruction if needed).
4877 do {
4878 if ((child->type == XML_ELEMENT_NODE) &&
4879 IS_XSLT_ELEM_FAST(child) &&
4880 IS_XSLT_NAME(child, "param"))
4882 XSLT_CCTXT(style)->inode->curChildType = XSLT_FUNC_PARAM;
4883 xsltParseAnyXSLTElem(XSLT_CCTXT(style), child);
4884 } else
4885 break;
4886 child = child->next;
4887 } while (child != NULL);
4889 * Parse the content and register the pattern.
4891 xsltParseSequenceConstructor(XSLT_CCTXT(style), child);
4895 #else /* XSLT_REFACTORED */
4898 * xsltParseTemplateContent:
4899 * @style: the XSLT stylesheet
4900 * @templ: the container node (can be a document for literal results)
4902 * parse a template content-model
4903 * Clean-up the template content from unwanted ignorable blank nodes
4904 * and process xslt:text
4906 void
4907 xsltParseTemplateContent(xsltStylesheetPtr style, xmlNodePtr templ) {
4908 xmlNodePtr cur, delete;
4910 if ((style == NULL) || (templ == NULL) ||
4911 (templ->type == XML_NAMESPACE_DECL)) return;
4914 * This content comes from the stylesheet
4915 * For stylesheets, the set of whitespace-preserving
4916 * element names consists of just xsl:text.
4918 cur = templ->children;
4919 delete = NULL;
4920 while (cur != NULL) {
4921 if (delete != NULL) {
4922 #ifdef WITH_XSLT_DEBUG_BLANKS
4923 xsltGenericDebug(xsltGenericDebugContext,
4924 "xsltParseTemplateContent: removing text\n");
4925 #endif
4926 xmlUnlinkNode(delete);
4927 xmlFreeNode(delete);
4928 delete = NULL;
4930 if (IS_XSLT_ELEM(cur)) {
4931 xsltStylePreCompute(style, cur);
4933 if (IS_XSLT_NAME(cur, "text")) {
4935 * TODO: Processing of xsl:text should be moved to
4936 * xsltPreprocessStylesheet(), since otherwise this
4937 * will be performed for every multiply included
4938 * stylesheet; i.e. this here is not skipped with
4939 * the use of the style->nopreproc flag.
4941 if (cur->children != NULL) {
4942 xmlChar *prop;
4943 xmlNodePtr text = cur->children, next;
4944 int noesc = 0;
4946 prop = xmlGetNsProp(cur,
4947 (const xmlChar *)"disable-output-escaping",
4948 NULL);
4949 if (prop != NULL) {
4950 #ifdef WITH_XSLT_DEBUG_PARSING
4951 xsltGenericDebug(xsltGenericDebugContext,
4952 "Disable escaping: %s\n", text->content);
4953 #endif
4954 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
4955 noesc = 1;
4956 } else if (!xmlStrEqual(prop,
4957 (const xmlChar *)"no")){
4958 xsltTransformError(NULL, style, cur,
4959 "xsl:text: disable-output-escaping allows only yes or no\n");
4960 style->warnings++;
4963 xmlFree(prop);
4966 while (text != NULL) {
4967 if (text->type == XML_COMMENT_NODE) {
4968 text = text->next;
4969 continue;
4971 if ((text->type != XML_TEXT_NODE) &&
4972 (text->type != XML_CDATA_SECTION_NODE)) {
4973 xsltTransformError(NULL, style, cur,
4974 "xsltParseTemplateContent: xslt:text content problem\n");
4975 style->errors++;
4976 break;
4978 if ((noesc) && (text->type != XML_CDATA_SECTION_NODE))
4979 text->name = xmlStringTextNoenc;
4980 text = text->next;
4984 * replace xsl:text by the list of childs
4986 if (text == NULL) {
4987 text = cur->children;
4988 while (text != NULL) {
4989 if ((style->internalized) &&
4990 (text->content != NULL) &&
4991 (!xmlDictOwns(style->dict, text->content))) {
4994 * internalize the text string
4996 if (text->doc->dict != NULL) {
4997 const xmlChar *tmp;
4999 tmp = xmlDictLookup(text->doc->dict,
5000 text->content, -1);
5001 if (tmp != text->content) {
5002 xmlNodeSetContent(text, NULL);
5003 text->content = (xmlChar *) tmp;
5008 next = text->next;
5009 xmlUnlinkNode(text);
5010 xmlAddPrevSibling(cur, text);
5011 text = next;
5015 delete = cur;
5016 goto skip_children;
5019 else if ((cur->ns != NULL) && (style->nsDefs != NULL) &&
5020 (xsltCheckExtPrefix(style, cur->ns->prefix)))
5023 * okay this is an extension element compile it too
5025 xsltStylePreCompute(style, cur);
5027 else if (cur->type == XML_ELEMENT_NODE)
5030 * This is an element which will be output as part of the
5031 * template exectution, precompile AVT if found.
5033 if ((cur->ns == NULL) && (style->defaultAlias != NULL)) {
5034 cur->ns = xmlSearchNsByHref(cur->doc, cur,
5035 style->defaultAlias);
5037 if (cur->properties != NULL) {
5038 xmlAttrPtr attr = cur->properties;
5040 while (attr != NULL) {
5041 xsltCompileAttr(style, attr);
5042 attr = attr->next;
5047 * Skip to next node
5049 if (cur->children != NULL) {
5050 if (cur->children->type != XML_ENTITY_DECL) {
5051 cur = cur->children;
5052 continue;
5055 skip_children:
5056 if (cur->next != NULL) {
5057 cur = cur->next;
5058 continue;
5061 do {
5062 cur = cur->parent;
5063 if (cur == NULL)
5064 break;
5065 if (cur == templ) {
5066 cur = NULL;
5067 break;
5069 if (cur->next != NULL) {
5070 cur = cur->next;
5071 break;
5073 } while (cur != NULL);
5075 if (delete != NULL) {
5076 #ifdef WITH_XSLT_DEBUG_PARSING
5077 xsltGenericDebug(xsltGenericDebugContext,
5078 "xsltParseTemplateContent: removing text\n");
5079 #endif
5080 xmlUnlinkNode(delete);
5081 xmlFreeNode(delete);
5082 delete = NULL;
5086 * Skip the first params
5088 cur = templ->children;
5089 while (cur != NULL) {
5090 if ((IS_XSLT_ELEM(cur)) && (!(IS_XSLT_NAME(cur, "param"))))
5091 break;
5092 cur = cur->next;
5096 * Browse the remainder of the template
5098 while (cur != NULL) {
5099 if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
5100 xmlNodePtr param = cur;
5102 xsltTransformError(NULL, style, cur,
5103 "xsltParseTemplateContent: ignoring misplaced param element\n");
5104 if (style != NULL) style->warnings++;
5105 cur = cur->next;
5106 xmlUnlinkNode(param);
5107 xmlFreeNode(param);
5108 } else
5109 break;
5113 #endif /* else XSLT_REFACTORED */
5116 * xsltParseStylesheetKey:
5117 * @style: the XSLT stylesheet
5118 * @key: the "key" element
5120 * <!-- Category: top-level-element -->
5121 * <xsl:key name = qname, match = pattern, use = expression />
5123 * parse an XSLT stylesheet key definition and register it
5126 static void
5127 xsltParseStylesheetKey(xsltStylesheetPtr style, xmlNodePtr key) {
5128 xmlChar *prop = NULL;
5129 xmlChar *use = NULL;
5130 xmlChar *match = NULL;
5131 xmlChar *name = NULL;
5132 xmlChar *nameURI = NULL;
5134 if ((style == NULL) || (key == NULL) || (key->type != XML_ELEMENT_NODE))
5135 return;
5138 * Get arguments
5140 prop = xmlGetNsProp(key, (const xmlChar *)"name", NULL);
5141 if (prop != NULL) {
5142 const xmlChar *URI;
5145 * TODO: Don't use xsltGetQNameURI().
5147 URI = xsltGetQNameURI(key, &prop);
5148 if (prop == NULL) {
5149 if (style != NULL) style->errors++;
5150 goto error;
5151 } else {
5152 name = prop;
5153 if (URI != NULL)
5154 nameURI = xmlStrdup(URI);
5156 #ifdef WITH_XSLT_DEBUG_PARSING
5157 xsltGenericDebug(xsltGenericDebugContext,
5158 "xsltParseStylesheetKey: name %s\n", name);
5159 #endif
5160 } else {
5161 xsltTransformError(NULL, style, key,
5162 "xsl:key : error missing name\n");
5163 if (style != NULL) style->errors++;
5164 goto error;
5167 match = xmlGetNsProp(key, (const xmlChar *)"match", NULL);
5168 if (match == NULL) {
5169 xsltTransformError(NULL, style, key,
5170 "xsl:key : error missing match\n");
5171 if (style != NULL) style->errors++;
5172 goto error;
5175 use = xmlGetNsProp(key, (const xmlChar *)"use", NULL);
5176 if (use == NULL) {
5177 xsltTransformError(NULL, style, key,
5178 "xsl:key : error missing use\n");
5179 if (style != NULL) style->errors++;
5180 goto error;
5184 * register the keys
5186 xsltAddKey(style, name, nameURI, match, use, key);
5189 error:
5190 if (use != NULL)
5191 xmlFree(use);
5192 if (match != NULL)
5193 xmlFree(match);
5194 if (name != NULL)
5195 xmlFree(name);
5196 if (nameURI != NULL)
5197 xmlFree(nameURI);
5199 if (key->children != NULL) {
5200 xsltParseContentError(style, key->children);
5204 #ifdef XSLT_REFACTORED
5206 * xsltParseXSLTTemplate:
5207 * @style: the XSLT stylesheet
5208 * @template: the "template" element
5210 * parse an XSLT stylesheet template building the associated structures
5211 * TODO: Is @style ever expected to be NULL?
5213 * Called from:
5214 * xsltParseXSLTStylesheet()
5215 * xsltParseStylesheetTop()
5218 static void
5219 xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) {
5220 xsltTemplatePtr templ;
5221 xmlChar *prop;
5222 double priority;
5224 if ((cctxt == NULL) || (templNode == NULL) ||
5225 (templNode->type != XML_ELEMENT_NODE))
5226 return;
5229 * Create and link the structure
5231 templ = xsltNewTemplate();
5232 if (templ == NULL)
5233 return;
5235 xsltCompilerNodePush(cctxt, templNode);
5236 if (templNode->nsDef != NULL)
5237 cctxt->inode->inScopeNs =
5238 xsltCompilerBuildInScopeNsList(cctxt, templNode);
5240 templ->next = cctxt->style->templates;
5241 cctxt->style->templates = templ;
5242 templ->style = cctxt->style;
5245 * Attribute "mode".
5247 prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL);
5248 if (prop != NULL) {
5249 const xmlChar *modeURI;
5252 * TODO: We need a standardized function for extraction
5253 * of namespace names and local names from QNames.
5254 * Don't use xsltGetQNameURI() as it cannot channe�
5255 * reports through the context.
5257 modeURI = xsltGetQNameURI(templNode, &prop);
5258 if (prop == NULL) {
5259 cctxt->style->errors++;
5260 goto error;
5262 templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1);
5263 xmlFree(prop);
5264 prop = NULL;
5265 if (xmlValidateNCName(templ->mode, 0)) {
5266 xsltTransformError(NULL, cctxt->style, templNode,
5267 "xsl:template: Attribute 'mode': The local part '%s' "
5268 "of the value is not a valid NCName.\n", templ->name);
5269 cctxt->style->errors++;
5270 goto error;
5272 if (modeURI != NULL)
5273 templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1);
5274 #ifdef WITH_XSLT_DEBUG_PARSING
5275 xsltGenericDebug(xsltGenericDebugContext,
5276 "xsltParseXSLTTemplate: mode %s\n", templ->mode);
5277 #endif
5280 * Attribute "match".
5282 prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL);
5283 if (prop != NULL) {
5284 templ->match = prop;
5285 prop = NULL;
5288 * Attribute "priority".
5290 prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL);
5291 if (prop != NULL) {
5292 priority = xmlXPathStringEvalNumber(prop);
5293 templ->priority = (float) priority;
5294 xmlFree(prop);
5295 prop = NULL;
5298 * Attribute "name".
5300 prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL);
5301 if (prop != NULL) {
5302 const xmlChar *nameURI;
5303 xsltTemplatePtr curTempl;
5306 * TODO: Don't use xsltGetQNameURI().
5308 nameURI = xsltGetQNameURI(templNode, &prop);
5309 if (prop == NULL) {
5310 cctxt->style->errors++;
5311 goto error;
5313 templ->name = xmlDictLookup(cctxt->style->dict, prop, -1);
5314 xmlFree(prop);
5315 prop = NULL;
5316 if (xmlValidateNCName(templ->name, 0)) {
5317 xsltTransformError(NULL, cctxt->style, templNode,
5318 "xsl:template: Attribute 'name': The local part '%s' of "
5319 "the value is not a valid NCName.\n", templ->name);
5320 cctxt->style->errors++;
5321 goto error;
5323 if (nameURI != NULL)
5324 templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1);
5325 curTempl = templ->next;
5326 while (curTempl != NULL) {
5327 if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) &&
5328 xmlStrEqual(curTempl->nameURI, nameURI) ) ||
5329 (nameURI == NULL && curTempl->nameURI == NULL &&
5330 xmlStrEqual(curTempl->name, templ->name)))
5332 xsltTransformError(NULL, cctxt->style, templNode,
5333 "xsl:template: error duplicate name '%s'\n", templ->name);
5334 cctxt->style->errors++;
5335 goto error;
5337 curTempl = curTempl->next;
5340 if (templNode->children != NULL) {
5341 xsltParseTemplateContent(cctxt->style, templNode);
5343 * MAYBE TODO: Custom behaviour: In order to stay compatible with
5344 * Xalan and MSXML(.NET), we could allow whitespace
5345 * to appear before an xml:param element; this whitespace
5346 * will additionally become part of the "template".
5347 * NOTE that this is totally deviates from the spec, but
5348 * is the de facto behaviour of Xalan and MSXML(.NET).
5349 * Personally I wouldn't allow this, since if we have:
5350 * <xsl:template ...xml:space="preserve">
5351 * <xsl:param name="foo"/>
5352 * <xsl:param name="bar"/>
5353 * <xsl:param name="zoo"/>
5354 * ... the whitespace between every xsl:param would be
5355 * added to the result tree.
5359 templ->elem = templNode;
5360 templ->content = templNode->children;
5361 xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI);
5363 error:
5364 xsltCompilerNodePop(cctxt, templNode);
5365 return;
5368 #else /* XSLT_REFACTORED */
5371 * xsltParseStylesheetTemplate:
5372 * @style: the XSLT stylesheet
5373 * @template: the "template" element
5375 * parse an XSLT stylesheet template building the associated structures
5378 static void
5379 xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) {
5380 xsltTemplatePtr ret;
5381 xmlChar *prop;
5382 xmlChar *mode = NULL;
5383 xmlChar *modeURI = NULL;
5384 double priority;
5386 if ((style == NULL) || (template == NULL) ||
5387 (template->type != XML_ELEMENT_NODE))
5388 return;
5391 * Create and link the structure
5393 ret = xsltNewTemplate();
5394 if (ret == NULL)
5395 return;
5396 ret->next = style->templates;
5397 style->templates = ret;
5398 ret->style = style;
5401 * Get inherited namespaces
5404 * TODO: Apply the optimized in-scope-namespace mechanism
5405 * as for the other XSLT instructions.
5407 xsltGetInheritedNsList(style, ret, template);
5410 * Get arguments
5412 prop = xmlGetNsProp(template, (const xmlChar *)"mode", NULL);
5413 if (prop != NULL) {
5414 const xmlChar *URI;
5417 * TODO: Don't use xsltGetQNameURI().
5419 URI = xsltGetQNameURI(template, &prop);
5420 if (prop == NULL) {
5421 if (style != NULL) style->errors++;
5422 goto error;
5423 } else {
5424 mode = prop;
5425 if (URI != NULL)
5426 modeURI = xmlStrdup(URI);
5428 ret->mode = xmlDictLookup(style->dict, mode, -1);
5429 ret->modeURI = xmlDictLookup(style->dict, modeURI, -1);
5430 #ifdef WITH_XSLT_DEBUG_PARSING
5431 xsltGenericDebug(xsltGenericDebugContext,
5432 "xsltParseStylesheetTemplate: mode %s\n", mode);
5433 #endif
5434 if (mode != NULL) xmlFree(mode);
5435 if (modeURI != NULL) xmlFree(modeURI);
5437 prop = xmlGetNsProp(template, (const xmlChar *)"match", NULL);
5438 if (prop != NULL) {
5439 if (ret->match != NULL) xmlFree(ret->match);
5440 ret->match = prop;
5443 prop = xmlGetNsProp(template, (const xmlChar *)"priority", NULL);
5444 if (prop != NULL) {
5445 priority = xmlXPathStringEvalNumber(prop);
5446 ret->priority = (float) priority;
5447 xmlFree(prop);
5450 prop = xmlGetNsProp(template, (const xmlChar *)"name", NULL);
5451 if (prop != NULL) {
5452 const xmlChar *URI;
5455 * TODO: Don't use xsltGetQNameURI().
5457 URI = xsltGetQNameURI(template, &prop);
5458 if (prop == NULL) {
5459 if (style != NULL) style->errors++;
5460 goto error;
5461 } else {
5462 if (xmlValidateNCName(prop,0)) {
5463 xsltTransformError(NULL, style, template,
5464 "xsl:template : error invalid name '%s'\n", prop);
5465 if (style != NULL) style->errors++;
5466 xmlFree(prop);
5467 goto error;
5469 ret->name = xmlDictLookup(style->dict, BAD_CAST prop, -1);
5470 xmlFree(prop);
5471 prop = NULL;
5472 if (URI != NULL)
5473 ret->nameURI = xmlDictLookup(style->dict, BAD_CAST URI, -1);
5474 else
5475 ret->nameURI = NULL;
5480 * parse the content and register the pattern
5482 xsltParseTemplateContent(style, template);
5483 ret->elem = template;
5484 ret->content = template->children;
5485 xsltAddTemplate(style, ret, ret->mode, ret->modeURI);
5487 error:
5488 return;
5491 #endif /* else XSLT_REFACTORED */
5493 #ifdef XSLT_REFACTORED
5496 * xsltIncludeComp:
5497 * @cctxt: the compilation context
5498 * @node: the xsl:include node
5500 * Process the xslt include node on the source node
5502 static xsltStyleItemIncludePtr
5503 xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) {
5504 xsltStyleItemIncludePtr item;
5506 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
5507 return(NULL);
5509 node->psvi = NULL;
5510 item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude));
5511 if (item == NULL) {
5512 xsltTransformError(NULL, cctxt->style, node,
5513 "xsltIncludeComp : malloc failed\n");
5514 cctxt->style->errors++;
5515 return(NULL);
5517 memset(item, 0, sizeof(xsltStyleItemInclude));
5519 node->psvi = item;
5520 item->inst = node;
5521 item->type = XSLT_FUNC_INCLUDE;
5523 item->next = cctxt->style->preComps;
5524 cctxt->style->preComps = (xsltElemPreCompPtr) item;
5526 return(item);
5530 * xsltParseFindTopLevelElem:
5532 static int
5533 xsltParseFindTopLevelElem(xsltCompilerCtxtPtr cctxt,
5534 xmlNodePtr cur,
5535 const xmlChar *name,
5536 const xmlChar *namespaceURI,
5537 int breakOnOtherElem,
5538 xmlNodePtr *resultNode)
5540 if (name == NULL)
5541 return(-1);
5543 *resultNode = NULL;
5544 while (cur != NULL) {
5545 if (cur->type == XML_ELEMENT_NODE) {
5546 if ((cur->ns != NULL) && (cur->name != NULL)) {
5547 if ((*(cur->name) == *name) &&
5548 xmlStrEqual(cur->name, name) &&
5549 xmlStrEqual(cur->ns->href, namespaceURI))
5551 *resultNode = cur;
5552 return(1);
5555 if (breakOnOtherElem)
5556 break;
5558 cur = cur->next;
5560 *resultNode = cur;
5561 return(0);
5564 static int
5565 xsltParseTopLevelXSLTElem(xsltCompilerCtxtPtr cctxt,
5566 xmlNodePtr node,
5567 xsltStyleType type)
5569 int ret = 0;
5572 * TODO: The reason why this function exists:
5573 * due to historical reasons some of the
5574 * top-level declarations are processed by functions
5575 * in other files. Since we need still to set
5576 * up the node-info and generate information like
5577 * in-scope namespaces, this is a wrapper around
5578 * those old parsing functions.
5580 xsltCompilerNodePush(cctxt, node);
5581 if (node->nsDef != NULL)
5582 cctxt->inode->inScopeNs =
5583 xsltCompilerBuildInScopeNsList(cctxt, node);
5584 cctxt->inode->type = type;
5586 switch (type) {
5587 case XSLT_FUNC_INCLUDE:
5589 int oldIsInclude;
5591 if (xsltCompileXSLTIncludeElem(cctxt, node) == NULL)
5592 goto exit;
5594 * Mark this stylesheet tree as being currently included.
5596 oldIsInclude = cctxt->isInclude;
5597 cctxt->isInclude = 1;
5599 if (xsltParseStylesheetInclude(cctxt->style, node) != 0) {
5600 cctxt->style->errors++;
5602 cctxt->isInclude = oldIsInclude;
5604 break;
5605 case XSLT_FUNC_PARAM:
5606 xsltStylePreCompute(cctxt->style, node);
5607 xsltParseGlobalParam(cctxt->style, node);
5608 break;
5609 case XSLT_FUNC_VARIABLE:
5610 xsltStylePreCompute(cctxt->style, node);
5611 xsltParseGlobalVariable(cctxt->style, node);
5612 break;
5613 case XSLT_FUNC_ATTRSET:
5614 xsltParseStylesheetAttributeSet(cctxt->style, node);
5615 break;
5616 default:
5617 xsltTransformError(NULL, cctxt->style, node,
5618 "Internal error: (xsltParseTopLevelXSLTElem) "
5619 "Cannot handle this top-level declaration.\n");
5620 cctxt->style->errors++;
5621 ret = -1;
5624 exit:
5625 xsltCompilerNodePop(cctxt, node);
5627 return(ret);
5630 #if 0
5631 static int
5632 xsltParseRemoveWhitespace(xmlNodePtr node)
5634 if ((node == NULL) || (node->children == NULL))
5635 return(0);
5636 else {
5637 xmlNodePtr delNode = NULL, child = node->children;
5639 do {
5640 if (delNode) {
5641 xmlUnlinkNode(delNode);
5642 xmlFreeNode(delNode);
5643 delNode = NULL;
5645 if (((child->type == XML_TEXT_NODE) ||
5646 (child->type == XML_CDATA_SECTION_NODE)) &&
5647 (IS_BLANK_NODE(child)))
5648 delNode = child;
5649 child = child->next;
5650 } while (child != NULL);
5651 if (delNode) {
5652 xmlUnlinkNode(delNode);
5653 xmlFreeNode(delNode);
5654 delNode = NULL;
5657 return(0);
5659 #endif
5661 static int
5662 xsltParseXSLTStylesheetElemCore(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
5664 #ifdef WITH_XSLT_DEBUG_PARSING
5665 int templates = 0;
5666 #endif
5667 xmlNodePtr cur, start = NULL;
5668 xsltStylesheetPtr style;
5670 if ((cctxt == NULL) || (node == NULL) ||
5671 (node->type != XML_ELEMENT_NODE))
5672 return(-1);
5674 style = cctxt->style;
5676 * At this stage all import declarations of all stylesheet modules
5677 * with the same stylesheet level have been processed.
5678 * Now we can safely parse the rest of the declarations.
5680 if (IS_XSLT_ELEM_FAST(node) && IS_XSLT_NAME(node, "include"))
5682 xsltDocumentPtr include;
5684 * URGENT TODO: Make this work with simplified stylesheets!
5685 * I.e., when we won't find an xsl:stylesheet element.
5688 * This is as include declaration.
5690 include = ((xsltStyleItemIncludePtr) node->psvi)->include;
5691 if (include == NULL) {
5692 /* TODO: raise error? */
5693 return(-1);
5696 * TODO: Actually an xsl:include should locate an embedded
5697 * stylesheet as well; so the document-element won't always
5698 * be the element where the actual stylesheet is rooted at.
5699 * But such embedded stylesheets are not supported by Libxslt yet.
5701 node = xmlDocGetRootElement(include->doc);
5702 if (node == NULL) {
5703 return(-1);
5707 if (node->children == NULL)
5708 return(0);
5710 * Push the xsl:stylesheet/xsl:transform element.
5712 xsltCompilerNodePush(cctxt, node);
5713 cctxt->inode->isRoot = 1;
5714 cctxt->inode->nsChanged = 0;
5716 * Start with the naked dummy info for literal result elements.
5718 cctxt->inode->litResElemInfo = cctxt->inodeList->litResElemInfo;
5721 * In every case, we need to have
5722 * the in-scope namespaces of the element, where the
5723 * stylesheet is rooted at, regardless if it's an XSLT
5724 * instruction or a literal result instruction (or if
5725 * this is an embedded stylesheet).
5727 cctxt->inode->inScopeNs =
5728 xsltCompilerBuildInScopeNsList(cctxt, node);
5731 * Process attributes of xsl:stylesheet/xsl:transform.
5732 * --------------------------------------------------
5733 * Allowed are:
5734 * id = id
5735 * extension-element-prefixes = tokens
5736 * exclude-result-prefixes = tokens
5737 * version = number (mandatory)
5739 if (xsltParseAttrXSLTVersion(cctxt, node,
5740 XSLT_ELEMENT_CATEGORY_XSLT) == 0)
5743 * Attribute "version".
5744 * XSLT 1.0: "An xsl:stylesheet element *must* have a version
5745 * attribute, indicating the version of XSLT that the
5746 * stylesheet requires".
5747 * The root element of a simplified stylesheet must also have
5748 * this attribute.
5750 #ifdef XSLT_REFACTORED_MANDATORY_VERSION
5751 if (isXsltElem)
5752 xsltTransformError(NULL, cctxt->style, node,
5753 "The attribute 'version' is missing.\n");
5754 cctxt->style->errors++;
5755 #else
5756 /* OLD behaviour. */
5757 xsltTransformError(NULL, cctxt->style, node,
5758 "xsl:version is missing: document may not be a stylesheet\n");
5759 cctxt->style->warnings++;
5760 #endif
5763 * The namespaces declared by the attributes
5764 * "extension-element-prefixes" and
5765 * "exclude-result-prefixes" are local to *this*
5766 * stylesheet tree; i.e., they are *not* visible to
5767 * other stylesheet-modules, whether imported or included.
5769 * Attribute "extension-element-prefixes".
5771 cctxt->inode->extElemNs =
5772 xsltParseExtElemPrefixes(cctxt, node, NULL,
5773 XSLT_ELEMENT_CATEGORY_XSLT);
5775 * Attribute "exclude-result-prefixes".
5777 cctxt->inode->exclResultNs =
5778 xsltParseExclResultPrefixes(cctxt, node, NULL,
5779 XSLT_ELEMENT_CATEGORY_XSLT);
5781 * Create/reuse info for the literal result element.
5783 if (cctxt->inode->nsChanged)
5784 xsltLREInfoCreate(cctxt, node, 0);
5786 * Processed top-level elements:
5787 * ----------------------------
5788 * xsl:variable, xsl:param (QName, in-scope ns,
5789 * expression (vars allowed))
5790 * xsl:attribute-set (QName, in-scope ns)
5791 * xsl:strip-space, xsl:preserve-space (XPath NameTests,
5792 * in-scope ns)
5793 * I *think* global scope, merge with includes
5794 * xsl:output (QName, in-scope ns)
5795 * xsl:key (QName, in-scope ns, pattern,
5796 * expression (vars *not* allowed))
5797 * xsl:decimal-format (QName, needs in-scope ns)
5798 * xsl:namespace-alias (in-scope ns)
5799 * global scope, merge with includes
5800 * xsl:template (last, QName, pattern)
5802 * (whitespace-only text-nodes have *not* been removed
5803 * yet; this will be done in xsltParseSequenceConstructor)
5805 * Report misplaced child-nodes first.
5807 cur = node->children;
5808 while (cur != NULL) {
5809 if (cur->type == XML_TEXT_NODE) {
5810 xsltTransformError(NULL, style, cur,
5811 "Misplaced text node (content: '%s').\n",
5812 (cur->content != NULL) ? cur->content : BAD_CAST "");
5813 style->errors++;
5814 } else if (cur->type != XML_ELEMENT_NODE) {
5815 xsltTransformError(NULL, style, cur, "Misplaced node.\n");
5816 style->errors++;
5818 cur = cur->next;
5821 * Skip xsl:import elements; they have been processed
5822 * already.
5824 cur = node->children;
5825 while ((cur != NULL) && xsltParseFindTopLevelElem(cctxt, cur,
5826 BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1)
5827 cur = cur->next;
5828 if (cur == NULL)
5829 goto exit;
5831 start = cur;
5833 * Process all top-level xsl:param elements.
5835 while ((cur != NULL) &&
5836 xsltParseFindTopLevelElem(cctxt, cur,
5837 BAD_CAST "param", XSLT_NAMESPACE, 0, &cur) == 1)
5839 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_PARAM);
5840 cur = cur->next;
5843 * Process all top-level xsl:variable elements.
5845 cur = start;
5846 while ((cur != NULL) &&
5847 xsltParseFindTopLevelElem(cctxt, cur,
5848 BAD_CAST "variable", XSLT_NAMESPACE, 0, &cur) == 1)
5850 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_VARIABLE);
5851 cur = cur->next;
5854 * Process all the rest of top-level elements.
5856 cur = start;
5857 while (cur != NULL) {
5859 * Process element nodes.
5861 if (cur->type == XML_ELEMENT_NODE) {
5862 if (cur->ns == NULL) {
5863 xsltTransformError(NULL, style, cur,
5864 "Unexpected top-level element in no namespace.\n");
5865 style->errors++;
5866 cur = cur->next;
5867 continue;
5870 * Process all XSLT elements.
5872 if (IS_XSLT_ELEM_FAST(cur)) {
5874 * xsl:import is only allowed at the beginning.
5876 if (IS_XSLT_NAME(cur, "import")) {
5877 xsltTransformError(NULL, style, cur,
5878 "Misplaced xsl:import element.\n");
5879 style->errors++;
5880 cur = cur->next;
5881 continue;
5884 * TODO: Change the return type of the parsing functions
5885 * to int.
5887 if (IS_XSLT_NAME(cur, "template")) {
5888 #ifdef WITH_XSLT_DEBUG_PARSING
5889 templates++;
5890 #endif
5892 * TODO: Is the position of xsl:template in the
5893 * tree significant? If not it would be easier to
5894 * parse them at a later stage.
5896 xsltParseXSLTTemplate(cctxt, cur);
5897 } else if (IS_XSLT_NAME(cur, "variable")) {
5898 /* NOP; done already */
5899 } else if (IS_XSLT_NAME(cur, "param")) {
5900 /* NOP; done already */
5901 } else if (IS_XSLT_NAME(cur, "include")) {
5902 if (cur->psvi != NULL)
5903 xsltParseXSLTStylesheetElemCore(cctxt, cur);
5904 else {
5905 xsltTransformError(NULL, style, cur,
5906 "Internal error: "
5907 "(xsltParseXSLTStylesheetElemCore) "
5908 "The xsl:include element was not compiled.\n");
5909 style->errors++;
5911 } else if (IS_XSLT_NAME(cur, "strip-space")) {
5912 /* No node info needed. */
5913 xsltParseStylesheetStripSpace(style, cur);
5914 } else if (IS_XSLT_NAME(cur, "preserve-space")) {
5915 /* No node info needed. */
5916 xsltParseStylesheetPreserveSpace(style, cur);
5917 } else if (IS_XSLT_NAME(cur, "output")) {
5918 /* No node-info needed. */
5919 xsltParseStylesheetOutput(style, cur);
5920 } else if (IS_XSLT_NAME(cur, "key")) {
5921 /* TODO: node-info needed for expressions ? */
5922 xsltParseStylesheetKey(style, cur);
5923 } else if (IS_XSLT_NAME(cur, "decimal-format")) {
5924 /* No node-info needed. */
5925 xsltParseStylesheetDecimalFormat(style, cur);
5926 } else if (IS_XSLT_NAME(cur, "attribute-set")) {
5927 xsltParseTopLevelXSLTElem(cctxt, cur,
5928 XSLT_FUNC_ATTRSET);
5929 } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
5930 /* NOP; done already */
5931 } else {
5932 if (cctxt->inode->forwardsCompat) {
5934 * Forwards-compatible mode:
5936 * XSLT-1: "if it is a top-level element and
5937 * XSLT 1.0 does not allow such elements as top-level
5938 * elements, then the element must be ignored along
5939 * with its content;"
5942 * TODO: I don't think we should generate a warning.
5944 xsltTransformError(NULL, style, cur,
5945 "Forwards-compatible mode: Ignoring unknown XSLT "
5946 "element '%s'.\n", cur->name);
5947 style->warnings++;
5948 } else {
5949 xsltTransformError(NULL, style, cur,
5950 "Unknown XSLT element '%s'.\n", cur->name);
5951 style->errors++;
5954 } else {
5955 xsltTopLevelFunction function;
5958 * Process non-XSLT elements, which are in a
5959 * non-NULL namespace.
5962 * QUESTION: What does xsltExtModuleTopLevelLookup()
5963 * do exactly?
5965 function = xsltExtModuleTopLevelLookup(cur->name,
5966 cur->ns->href);
5967 if (function != NULL)
5968 function(style, cur);
5969 #ifdef WITH_XSLT_DEBUG_PARSING
5970 xsltGenericDebug(xsltGenericDebugContext,
5971 "xsltParseXSLTStylesheetElemCore : User-defined "
5972 "data element '%s'.\n", cur->name);
5973 #endif
5976 cur = cur->next;
5979 exit:
5981 #ifdef WITH_XSLT_DEBUG_PARSING
5982 xsltGenericDebug(xsltGenericDebugContext,
5983 "### END of parsing top-level elements of doc '%s'.\n",
5984 node->doc->URL);
5985 xsltGenericDebug(xsltGenericDebugContext,
5986 "### Templates: %d\n", templates);
5987 #ifdef XSLT_REFACTORED
5988 xsltGenericDebug(xsltGenericDebugContext,
5989 "### Max inodes: %d\n", cctxt->maxNodeInfos);
5990 xsltGenericDebug(xsltGenericDebugContext,
5991 "### Max LREs : %d\n", cctxt->maxLREs);
5992 #endif /* XSLT_REFACTORED */
5993 #endif /* WITH_XSLT_DEBUG_PARSING */
5995 xsltCompilerNodePop(cctxt, node);
5996 return(0);
6000 * xsltParseXSLTStylesheet:
6001 * @cctxt: the compiler context
6002 * @node: the xsl:stylesheet/xsl:transform element-node
6004 * Parses the xsl:stylesheet and xsl:transform element.
6006 * <xsl:stylesheet
6007 * id = id
6008 * extension-element-prefixes = tokens
6009 * exclude-result-prefixes = tokens
6010 * version = number>
6011 * <!-- Content: (xsl:import*, top-level-elements) -->
6012 * </xsl:stylesheet>
6014 * BIG TODO: The xsl:include stuff.
6016 * Called by xsltParseStylesheetTree()
6018 * Returns 0 on success, a positive result on errors and
6019 * -1 on API or internal errors.
6021 static int
6022 xsltParseXSLTStylesheetElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
6024 xmlNodePtr cur, start;
6026 if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
6027 return(-1);
6029 if (node->children == NULL)
6030 goto exit;
6033 * Process top-level elements:
6034 * xsl:import (must be first)
6035 * xsl:include (this is just a pre-processing)
6037 cur = node->children;
6039 * Process xsl:import elements.
6040 * XSLT 1.0: "The xsl:import element children must precede all
6041 * other element children of an xsl:stylesheet element,
6042 * including any xsl:include element children."
6044 while ((cur != NULL) &&
6045 xsltParseFindTopLevelElem(cctxt, cur,
6046 BAD_CAST "import", XSLT_NAMESPACE, 1, &cur) == 1)
6048 if (xsltParseStylesheetImport(cctxt->style, cur) != 0) {
6049 cctxt->style->errors++;
6051 cur = cur->next;
6053 if (cur == NULL)
6054 goto exit;
6055 start = cur;
6057 * Pre-process all xsl:include elements.
6059 cur = start;
6060 while ((cur != NULL) &&
6061 xsltParseFindTopLevelElem(cctxt, cur,
6062 BAD_CAST "include", XSLT_NAMESPACE, 0, &cur) == 1)
6064 xsltParseTopLevelXSLTElem(cctxt, cur, XSLT_FUNC_INCLUDE);
6065 cur = cur->next;
6068 * Pre-process all xsl:namespace-alias elements.
6069 * URGENT TODO: This won't work correctly: the order of included
6070 * aliases and aliases defined here is significant.
6072 cur = start;
6073 while ((cur != NULL) &&
6074 xsltParseFindTopLevelElem(cctxt, cur,
6075 BAD_CAST "namespace-alias", XSLT_NAMESPACE, 0, &cur) == 1)
6077 xsltNamespaceAlias(cctxt->style, cur);
6078 cur = cur->next;
6081 if (cctxt->isInclude) {
6083 * If this stylesheet is intended for inclusion, then
6084 * we will process only imports and includes.
6086 goto exit;
6089 * Now parse the rest of the top-level elements.
6091 xsltParseXSLTStylesheetElemCore(cctxt, node);
6092 exit:
6094 return(0);
6097 #else /* XSLT_REFACTORED */
6100 * xsltParseStylesheetTop:
6101 * @style: the XSLT stylesheet
6102 * @top: the top level "stylesheet" or "transform" element
6104 * scan the top level elements of an XSL stylesheet
6106 static void
6107 xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) {
6108 xmlNodePtr cur;
6109 xmlChar *prop;
6110 #ifdef WITH_XSLT_DEBUG_PARSING
6111 int templates = 0;
6112 #endif
6114 if ((top == NULL) || (top->type != XML_ELEMENT_NODE))
6115 return;
6117 prop = xmlGetNsProp(top, (const xmlChar *)"version", NULL);
6118 if (prop == NULL) {
6119 xsltTransformError(NULL, style, top,
6120 "xsl:version is missing: document may not be a stylesheet\n");
6121 if (style != NULL) style->warnings++;
6122 } else {
6123 if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) &&
6124 (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) {
6125 xsltTransformError(NULL, style, top,
6126 "xsl:version: only 1.1 features are supported\n");
6127 if (style != NULL) {
6128 style->forwards_compatible = 1;
6129 style->warnings++;
6132 xmlFree(prop);
6136 * process xsl:import elements
6138 cur = top->children;
6139 while (cur != NULL) {
6140 if (IS_BLANK_NODE(cur)) {
6141 cur = cur->next;
6142 continue;
6144 if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "import")) {
6145 if (xsltParseStylesheetImport(style, cur) != 0)
6146 if (style != NULL) style->errors++;
6147 } else
6148 break;
6149 cur = cur->next;
6153 * process other top-level elements
6155 while (cur != NULL) {
6156 if (IS_BLANK_NODE(cur)) {
6157 cur = cur->next;
6158 continue;
6160 if (cur->type == XML_TEXT_NODE) {
6161 if (cur->content != NULL) {
6162 xsltTransformError(NULL, style, cur,
6163 "misplaced text node: '%s'\n", cur->content);
6165 if (style != NULL) style->errors++;
6166 cur = cur->next;
6167 continue;
6169 if ((cur->type == XML_ELEMENT_NODE) && (cur->ns == NULL)) {
6170 xsltGenericError(xsltGenericErrorContext,
6171 "Found a top-level element %s with null namespace URI\n",
6172 cur->name);
6173 if (style != NULL) style->errors++;
6174 cur = cur->next;
6175 continue;
6177 if ((cur->type == XML_ELEMENT_NODE) && (!(IS_XSLT_ELEM(cur)))) {
6178 xsltTopLevelFunction function;
6180 function = xsltExtModuleTopLevelLookup(cur->name,
6181 cur->ns->href);
6182 if (function != NULL)
6183 function(style, cur);
6185 #ifdef WITH_XSLT_DEBUG_PARSING
6186 xsltGenericDebug(xsltGenericDebugContext,
6187 "xsltParseStylesheetTop : found foreign element %s\n",
6188 cur->name);
6189 #endif
6190 cur = cur->next;
6191 continue;
6193 if (IS_XSLT_NAME(cur, "import")) {
6194 xsltTransformError(NULL, style, cur,
6195 "xsltParseStylesheetTop: ignoring misplaced import element\n");
6196 if (style != NULL) style->errors++;
6197 } else if (IS_XSLT_NAME(cur, "include")) {
6198 if (xsltParseStylesheetInclude(style, cur) != 0)
6199 if (style != NULL) style->errors++;
6200 } else if (IS_XSLT_NAME(cur, "strip-space")) {
6201 xsltParseStylesheetStripSpace(style, cur);
6202 } else if (IS_XSLT_NAME(cur, "preserve-space")) {
6203 xsltParseStylesheetPreserveSpace(style, cur);
6204 } else if (IS_XSLT_NAME(cur, "output")) {
6205 xsltParseStylesheetOutput(style, cur);
6206 } else if (IS_XSLT_NAME(cur, "key")) {
6207 xsltParseStylesheetKey(style, cur);
6208 } else if (IS_XSLT_NAME(cur, "decimal-format")) {
6209 xsltParseStylesheetDecimalFormat(style, cur);
6210 } else if (IS_XSLT_NAME(cur, "attribute-set")) {
6211 xsltParseStylesheetAttributeSet(style, cur);
6212 } else if (IS_XSLT_NAME(cur, "variable")) {
6213 xsltParseGlobalVariable(style, cur);
6214 } else if (IS_XSLT_NAME(cur, "param")) {
6215 xsltParseGlobalParam(style, cur);
6216 } else if (IS_XSLT_NAME(cur, "template")) {
6217 #ifdef WITH_XSLT_DEBUG_PARSING
6218 templates++;
6219 #endif
6220 xsltParseStylesheetTemplate(style, cur);
6221 } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
6222 xsltNamespaceAlias(style, cur);
6223 } else {
6224 if ((style != NULL) && (style->forwards_compatible == 0)) {
6225 xsltTransformError(NULL, style, cur,
6226 "xsltParseStylesheetTop: unknown %s element\n",
6227 cur->name);
6228 if (style != NULL) style->errors++;
6231 cur = cur->next;
6233 #ifdef WITH_XSLT_DEBUG_PARSING
6234 xsltGenericDebug(xsltGenericDebugContext,
6235 "parsed %d templates\n", templates);
6236 #endif
6239 #endif /* else of XSLT_REFACTORED */
6241 #ifdef XSLT_REFACTORED
6243 * xsltParseSimplifiedStylesheetTree:
6245 * @style: the stylesheet (TODO: Change this to the compiler context)
6246 * @doc: the document containing the stylesheet.
6247 * @node: the node where the stylesheet is rooted at
6249 * Returns 0 in case of success, a positive result if an error occurred
6250 * and -1 on API and internal errors.
6252 static int
6253 xsltParseSimplifiedStylesheetTree(xsltCompilerCtxtPtr cctxt,
6254 xmlDocPtr doc,
6255 xmlNodePtr node)
6257 xsltTemplatePtr templ;
6259 if ((cctxt == NULL) || (node == NULL))
6260 return(-1);
6262 if (xsltParseAttrXSLTVersion(cctxt, node, 0) == XSLT_ELEMENT_CATEGORY_LRE)
6265 * TODO: Adjust report, since this might be an
6266 * embedded stylesheet.
6268 xsltTransformError(NULL, cctxt->style, node,
6269 "The attribute 'xsl:version' is missing; cannot identify "
6270 "this document as an XSLT stylesheet document.\n");
6271 cctxt->style->errors++;
6272 return(1);
6275 #ifdef WITH_XSLT_DEBUG_PARSING
6276 xsltGenericDebug(xsltGenericDebugContext,
6277 "xsltParseSimplifiedStylesheetTree: document is stylesheet\n");
6278 #endif
6281 * Create and link the template
6283 templ = xsltNewTemplate();
6284 if (templ == NULL) {
6285 return(-1);
6287 templ->next = cctxt->style->templates;
6288 cctxt->style->templates = templ;
6289 templ->match = xmlStrdup(BAD_CAST "/");
6292 * Note that we push the document-node in this special case.
6294 xsltCompilerNodePush(cctxt, (xmlNodePtr) doc);
6296 * In every case, we need to have
6297 * the in-scope namespaces of the element, where the
6298 * stylesheet is rooted at, regardless if it's an XSLT
6299 * instruction or a literal result instruction (or if
6300 * this is an embedded stylesheet).
6302 cctxt->inode->inScopeNs =
6303 xsltCompilerBuildInScopeNsList(cctxt, node);
6305 * Parse the content and register the match-pattern.
6307 xsltParseSequenceConstructor(cctxt, node);
6308 xsltCompilerNodePop(cctxt, (xmlNodePtr) doc);
6310 templ->elem = (xmlNodePtr) doc;
6311 templ->content = node;
6312 xsltAddTemplate(cctxt->style, templ, NULL, NULL);
6313 cctxt->style->literal_result = 1;
6314 return(0);
6317 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
6319 * xsltRestoreDocumentNamespaces:
6320 * @ns: map of namespaces
6321 * @doc: the document
6323 * Restore the namespaces for the document
6325 * Returns 0 in case of success, -1 in case of failure
6328 xsltRestoreDocumentNamespaces(xsltNsMapPtr ns, xmlDocPtr doc)
6330 if (doc == NULL)
6331 return(-1);
6333 * Revert the changes we have applied to the namespace-URIs of
6334 * ns-decls.
6336 while (ns != NULL) {
6337 if ((ns->doc == doc) && (ns->ns != NULL)) {
6338 ns->ns->href = ns->origNsName;
6339 ns->origNsName = NULL;
6340 ns->ns = NULL;
6342 ns = ns->next;
6344 return(0);
6346 #endif /* XSLT_REFACTORED_XSLT_NSCOMP */
6349 * xsltParseStylesheetProcess:
6350 * @style: the XSLT stylesheet (the current stylesheet-level)
6351 * @doc: and xmlDoc parsed XML
6353 * Parses an XSLT stylesheet, adding the associated structures.
6354 * Called by:
6355 * xsltParseStylesheetImportedDoc() (xslt.c)
6356 * xsltParseStylesheetInclude() (imports.c)
6358 * Returns the value of the @style parameter if everything
6359 * went right, NULL if something went amiss.
6361 xsltStylesheetPtr
6362 xsltParseStylesheetProcess(xsltStylesheetPtr style, xmlDocPtr doc)
6364 xsltCompilerCtxtPtr cctxt;
6365 xmlNodePtr cur;
6366 int oldIsSimplifiedStylesheet;
6368 xsltInitGlobals();
6370 if ((style == NULL) || (doc == NULL))
6371 return(NULL);
6373 cctxt = XSLT_CCTXT(style);
6375 cur = xmlDocGetRootElement(doc);
6376 if (cur == NULL) {
6377 xsltTransformError(NULL, style, (xmlNodePtr) doc,
6378 "xsltParseStylesheetProcess : empty stylesheet\n");
6379 return(NULL);
6381 oldIsSimplifiedStylesheet = cctxt->simplified;
6383 if ((IS_XSLT_ELEM(cur)) &&
6384 ((IS_XSLT_NAME(cur, "stylesheet")) ||
6385 (IS_XSLT_NAME(cur, "transform")))) {
6386 #ifdef WITH_XSLT_DEBUG_PARSING
6387 xsltGenericDebug(xsltGenericDebugContext,
6388 "xsltParseStylesheetProcess : found stylesheet\n");
6389 #endif
6390 cctxt->simplified = 0;
6391 style->literal_result = 0;
6392 } else {
6393 cctxt->simplified = 1;
6394 style->literal_result = 1;
6397 * Pre-process the stylesheet if not already done before.
6398 * This will remove PIs and comments, merge adjacent
6399 * text nodes, internalize strings, etc.
6401 if (! style->nopreproc)
6402 xsltParsePreprocessStylesheetTree(cctxt, cur);
6404 * Parse and compile the stylesheet.
6406 if (style->literal_result == 0) {
6407 if (xsltParseXSLTStylesheetElem(cctxt, cur) != 0)
6408 return(NULL);
6409 } else {
6410 if (xsltParseSimplifiedStylesheetTree(cctxt, doc, cur) != 0)
6411 return(NULL);
6414 cctxt->simplified = oldIsSimplifiedStylesheet;
6416 return(style);
6419 #else /* XSLT_REFACTORED */
6422 * xsltParseStylesheetProcess:
6423 * @ret: the XSLT stylesheet (the current stylesheet-level)
6424 * @doc: and xmlDoc parsed XML
6426 * Parses an XSLT stylesheet, adding the associated structures.
6427 * Called by:
6428 * xsltParseStylesheetImportedDoc() (xslt.c)
6429 * xsltParseStylesheetInclude() (imports.c)
6431 * Returns the value of the @style parameter if everything
6432 * went right, NULL if something went amiss.
6434 xsltStylesheetPtr
6435 xsltParseStylesheetProcess(xsltStylesheetPtr ret, xmlDocPtr doc) {
6436 xmlNodePtr cur;
6438 xsltInitGlobals();
6440 if (doc == NULL)
6441 return(NULL);
6442 if (ret == NULL)
6443 return(ret);
6446 * First steps, remove blank nodes,
6447 * locate the xsl:stylesheet element and the
6448 * namespace declaration.
6450 cur = xmlDocGetRootElement(doc);
6451 if (cur == NULL) {
6452 xsltTransformError(NULL, ret, (xmlNodePtr) doc,
6453 "xsltParseStylesheetProcess : empty stylesheet\n");
6454 return(NULL);
6457 if ((IS_XSLT_ELEM(cur)) &&
6458 ((IS_XSLT_NAME(cur, "stylesheet")) ||
6459 (IS_XSLT_NAME(cur, "transform")))) {
6460 #ifdef WITH_XSLT_DEBUG_PARSING
6461 xsltGenericDebug(xsltGenericDebugContext,
6462 "xsltParseStylesheetProcess : found stylesheet\n");
6463 #endif
6464 ret->literal_result = 0;
6465 xsltParseStylesheetExcludePrefix(ret, cur, 1);
6466 xsltParseStylesheetExtPrefix(ret, cur, 1);
6467 } else {
6468 xsltParseStylesheetExcludePrefix(ret, cur, 0);
6469 xsltParseStylesheetExtPrefix(ret, cur, 0);
6470 ret->literal_result = 1;
6472 if (!ret->nopreproc) {
6473 xsltPreprocessStylesheet(ret, cur);
6475 if (ret->literal_result == 0) {
6476 xsltParseStylesheetTop(ret, cur);
6477 } else {
6478 xmlChar *prop;
6479 xsltTemplatePtr template;
6482 * the document itself might be the template, check xsl:version
6484 prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE);
6485 if (prop == NULL) {
6486 xsltTransformError(NULL, ret, cur,
6487 "xsltParseStylesheetProcess : document is not a stylesheet\n");
6488 return(NULL);
6491 #ifdef WITH_XSLT_DEBUG_PARSING
6492 xsltGenericDebug(xsltGenericDebugContext,
6493 "xsltParseStylesheetProcess : document is stylesheet\n");
6494 #endif
6496 if ((!xmlStrEqual(prop, (const xmlChar *)"1.0")) &&
6497 (!xmlStrEqual(prop, (const xmlChar *)"1.1"))) {
6498 xsltTransformError(NULL, ret, cur,
6499 "xsl:version: only 1.1 features are supported\n");
6500 ret->forwards_compatible = 1;
6501 ret->warnings++;
6503 xmlFree(prop);
6506 * Create and link the template
6508 template = xsltNewTemplate();
6509 if (template == NULL) {
6510 return(NULL);
6512 template->next = ret->templates;
6513 ret->templates = template;
6514 template->match = xmlStrdup((const xmlChar *)"/");
6517 * parse the content and register the pattern
6519 xsltParseTemplateContent(ret, (xmlNodePtr) doc);
6520 template->elem = (xmlNodePtr) doc;
6521 template->content = doc->children;
6522 xsltAddTemplate(ret, template, NULL, NULL);
6523 ret->literal_result = 1;
6526 return(ret);
6529 #endif /* else of XSLT_REFACTORED */
6532 * xsltParseStylesheetImportedDoc:
6533 * @doc: an xmlDoc parsed XML
6534 * @parentStyle: pointer to the parent stylesheet (if it exists)
6536 * parse an XSLT stylesheet building the associated structures
6537 * except the processing not needed for imported documents.
6539 * Returns a new XSLT stylesheet structure.
6542 xsltStylesheetPtr
6543 xsltParseStylesheetImportedDoc(xmlDocPtr doc,
6544 xsltStylesheetPtr parentStyle) {
6545 xsltStylesheetPtr retStyle;
6547 if (doc == NULL)
6548 return(NULL);
6550 retStyle = xsltNewStylesheetInternal(parentStyle);
6551 if (retStyle == NULL)
6552 return(NULL);
6554 if (xsltParseStylesheetUser(retStyle, doc) != 0) {
6555 xsltFreeStylesheet(retStyle);
6556 return(NULL);
6559 return(retStyle);
6563 * xsltParseStylesheetUser:
6564 * @style: pointer to the stylesheet
6565 * @doc: an xmlDoc parsed XML
6567 * Parse an XSLT stylesheet with a user-provided stylesheet struct.
6569 * Returns 0 if successful, -1 in case of error.
6572 xsltParseStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc) {
6573 if ((style == NULL) || (doc == NULL))
6574 return(-1);
6577 * Adjust the string dict.
6579 if (doc->dict != NULL) {
6580 xmlDictFree(style->dict);
6581 style->dict = doc->dict;
6582 #ifdef WITH_XSLT_DEBUG
6583 xsltGenericDebug(xsltGenericDebugContext,
6584 "reusing dictionary from %s for stylesheet\n",
6585 doc->URL);
6586 #endif
6587 xmlDictReference(style->dict);
6591 * TODO: Eliminate xsltGatherNamespaces(); we must not restrict
6592 * the stylesheet to containt distinct namespace prefixes.
6594 xsltGatherNamespaces(style);
6596 #ifdef XSLT_REFACTORED
6598 xsltCompilerCtxtPtr cctxt;
6599 xsltStylesheetPtr oldCurSheet;
6601 if (style->parent == NULL) {
6602 xsltPrincipalStylesheetDataPtr principalData;
6604 * Create extra data for the principal stylesheet.
6606 principalData = xsltNewPrincipalStylesheetData();
6607 if (principalData == NULL) {
6608 return(-1);
6610 style->principalData = principalData;
6612 * Create the compilation context
6613 * ------------------------------
6614 * (only once; for the principal stylesheet).
6615 * This is currently the only function where the
6616 * compilation context is created.
6618 cctxt = xsltCompilationCtxtCreate(style);
6619 if (cctxt == NULL) {
6620 return(-1);
6622 style->compCtxt = (void *) cctxt;
6623 cctxt->style = style;
6624 cctxt->dict = style->dict;
6625 cctxt->psData = principalData;
6627 * Push initial dummy node info.
6629 cctxt->depth = -1;
6630 xsltCompilerNodePush(cctxt, (xmlNodePtr) doc);
6631 } else {
6633 * Imported stylesheet.
6635 cctxt = style->parent->compCtxt;
6636 style->compCtxt = cctxt;
6639 * Save the old and set the current stylesheet structure in the
6640 * compilation context.
6642 oldCurSheet = cctxt->style;
6643 cctxt->style = style;
6645 style->doc = doc;
6646 xsltParseStylesheetProcess(style, doc);
6648 cctxt->style = oldCurSheet;
6649 if (style->parent == NULL) {
6651 * Pop the initial dummy node info.
6653 xsltCompilerNodePop(cctxt, (xmlNodePtr) doc);
6654 } else {
6656 * Clear the compilation context of imported
6657 * stylesheets.
6658 * TODO: really?
6660 /* style->compCtxt = NULL; */
6663 #ifdef XSLT_REFACTORED_XSLT_NSCOMP
6664 if (style->errors != 0) {
6666 * Restore all changes made to namespace URIs of ns-decls.
6668 if (cctxt->psData->nsMap)
6669 xsltRestoreDocumentNamespaces(cctxt->psData->nsMap, doc);
6671 #endif
6673 if (style->parent == NULL) {
6674 xsltCompilationCtxtFree(style->compCtxt);
6675 style->compCtxt = NULL;
6679 #else /* XSLT_REFACTORED */
6681 * Old behaviour.
6683 style->doc = doc;
6684 if (xsltParseStylesheetProcess(style, doc) == NULL) {
6685 style->doc = NULL;
6686 return(-1);
6688 #endif /* else of XSLT_REFACTORED */
6690 if (style->errors != 0) {
6692 * Detach the doc from the stylesheet; otherwise the doc
6693 * will be freed in xsltFreeStylesheet().
6695 style->doc = NULL;
6697 * Cleanup the doc if its the main stylesheet.
6699 if (style->parent == NULL)
6700 xsltCleanupStylesheetTree(doc, xmlDocGetRootElement(doc));
6701 return(-1);
6704 if (style->parent == NULL)
6705 xsltResolveStylesheetAttributeSet(style);
6707 return(0);
6711 * xsltParseStylesheetDoc:
6712 * @doc: and xmlDoc parsed XML
6714 * parse an XSLT stylesheet, building the associated structures. doc
6715 * is kept as a reference within the returned stylesheet, so changes
6716 * to doc after the parsing will be reflected when the stylesheet
6717 * is applied, and the doc is automatically freed when the
6718 * stylesheet is closed.
6720 * Returns a new XSLT stylesheet structure.
6723 xsltStylesheetPtr
6724 xsltParseStylesheetDoc(xmlDocPtr doc) {
6725 xsltInitGlobals();
6727 return(xsltParseStylesheetImportedDoc(doc, NULL));
6731 * xsltParseStylesheetFile:
6732 * @filename: the filename/URL to the stylesheet
6734 * Load and parse an XSLT stylesheet
6736 * Returns a new XSLT stylesheet structure.
6739 xsltStylesheetPtr
6740 xsltParseStylesheetFile(const xmlChar* filename) {
6741 xsltSecurityPrefsPtr sec;
6742 xsltStylesheetPtr ret;
6743 xmlDocPtr doc;
6745 xsltInitGlobals();
6747 if (filename == NULL)
6748 return(NULL);
6750 #ifdef WITH_XSLT_DEBUG_PARSING
6751 xsltGenericDebug(xsltGenericDebugContext,
6752 "xsltParseStylesheetFile : parse %s\n", filename);
6753 #endif
6756 * Security framework check
6758 sec = xsltGetDefaultSecurityPrefs();
6759 if (sec != NULL) {
6760 int res;
6762 res = xsltCheckRead(sec, NULL, filename);
6763 if (res <= 0) {
6764 if (res == 0)
6765 xsltTransformError(NULL, NULL, NULL,
6766 "xsltParseStylesheetFile: read rights for %s denied\n",
6767 filename);
6768 return(NULL);
6772 doc = xsltDocDefaultLoader(filename, NULL, XSLT_PARSE_OPTIONS,
6773 NULL, XSLT_LOAD_START);
6774 if (doc == NULL) {
6775 xsltTransformError(NULL, NULL, NULL,
6776 "xsltParseStylesheetFile : cannot parse %s\n", filename);
6777 return(NULL);
6779 ret = xsltParseStylesheetDoc(doc);
6780 if (ret == NULL) {
6781 xmlFreeDoc(doc);
6782 return(NULL);
6785 return(ret);
6788 /************************************************************************
6790 * Handling of Stylesheet PI *
6792 ************************************************************************/
6794 #define CUR (*cur)
6795 #define SKIP(val) cur += (val)
6796 #define NXT(val) cur[(val)]
6797 #define SKIP_BLANKS \
6798 while (IS_BLANK(CUR)) NEXT
6799 #define NEXT ((*cur) ? cur++ : cur)
6802 * xsltParseStylesheetPI:
6803 * @value: the value of the PI
6805 * This function checks that the type is text/xml and extracts
6806 * the URI-Reference for the stylesheet
6808 * Returns the URI-Reference for the stylesheet or NULL (it need to
6809 * be freed by the caller)
6811 static xmlChar *
6812 xsltParseStylesheetPI(const xmlChar *value) {
6813 const xmlChar *cur;
6814 const xmlChar *start;
6815 xmlChar *val;
6816 xmlChar tmp;
6817 xmlChar *href = NULL;
6818 int isXml = 0;
6820 if (value == NULL)
6821 return(NULL);
6823 cur = value;
6824 while (CUR != 0) {
6825 SKIP_BLANKS;
6826 if ((CUR == 't') && (NXT(1) == 'y') && (NXT(2) == 'p') &&
6827 (NXT(3) == 'e')) {
6828 SKIP(4);
6829 SKIP_BLANKS;
6830 if (CUR != '=')
6831 continue;
6832 NEXT;
6833 if ((CUR != '\'') && (CUR != '"'))
6834 continue;
6835 tmp = CUR;
6836 NEXT;
6837 start = cur;
6838 while ((CUR != 0) && (CUR != tmp))
6839 NEXT;
6840 if (CUR != tmp)
6841 continue;
6842 val = xmlStrndup(start, cur - start);
6843 NEXT;
6844 if (val == NULL)
6845 return(NULL);
6846 if ((xmlStrcasecmp(val, BAD_CAST "text/xml")) &&
6847 (xmlStrcasecmp(val, BAD_CAST "text/xsl"))) {
6848 xmlFree(val);
6849 break;
6851 isXml = 1;
6852 xmlFree(val);
6853 } else if ((CUR == 'h') && (NXT(1) == 'r') && (NXT(2) == 'e') &&
6854 (NXT(3) == 'f')) {
6855 SKIP(4);
6856 SKIP_BLANKS;
6857 if (CUR != '=')
6858 continue;
6859 NEXT;
6860 if ((CUR != '\'') && (CUR != '"'))
6861 continue;
6862 tmp = CUR;
6863 NEXT;
6864 start = cur;
6865 while ((CUR != 0) && (CUR != tmp))
6866 NEXT;
6867 if (CUR != tmp)
6868 continue;
6869 if (href == NULL)
6870 href = xmlStrndup(start, cur - start);
6871 NEXT;
6872 } else {
6873 while ((CUR != 0) && (!IS_BLANK(CUR)))
6874 NEXT;
6879 if (!isXml) {
6880 if (href != NULL)
6881 xmlFree(href);
6882 href = NULL;
6884 return(href);
6888 * xsltLoadStylesheetPI:
6889 * @doc: a document to process
6891 * This function tries to locate the stylesheet PI in the given document
6892 * If found, and if contained within the document, it will extract
6893 * that subtree to build the stylesheet to process @doc (doc itself will
6894 * be modified). If found but referencing an external document it will
6895 * attempt to load it and generate a stylesheet from it. In both cases,
6896 * the resulting stylesheet and the document need to be freed once the
6897 * transformation is done.
6899 * Returns a new XSLT stylesheet structure or NULL if not found.
6901 xsltStylesheetPtr
6902 xsltLoadStylesheetPI(xmlDocPtr doc) {
6903 xmlNodePtr child;
6904 xsltStylesheetPtr ret = NULL;
6905 xmlChar *href = NULL;
6906 xmlURIPtr URI;
6908 xsltInitGlobals();
6910 if (doc == NULL)
6911 return(NULL);
6914 * Find the text/xml stylesheet PI id any before the root
6916 child = doc->children;
6917 while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
6918 if ((child->type == XML_PI_NODE) &&
6919 (xmlStrEqual(child->name, BAD_CAST "xml-stylesheet"))) {
6920 href = xsltParseStylesheetPI(child->content);
6921 if (href != NULL)
6922 break;
6924 child = child->next;
6928 * If found check the href to select processing
6930 if (href != NULL) {
6931 #ifdef WITH_XSLT_DEBUG_PARSING
6932 xsltGenericDebug(xsltGenericDebugContext,
6933 "xsltLoadStylesheetPI : found PI href=%s\n", href);
6934 #endif
6935 URI = xmlParseURI((const char *) href);
6936 if (URI == NULL) {
6937 xsltTransformError(NULL, NULL, child,
6938 "xml-stylesheet : href %s is not valid\n", href);
6939 xmlFree(href);
6940 return(NULL);
6942 if ((URI->fragment != NULL) && (URI->scheme == NULL) &&
6943 (URI->opaque == NULL) && (URI->authority == NULL) &&
6944 (URI->server == NULL) && (URI->user == NULL) &&
6945 (URI->path == NULL) && (URI->query == NULL)) {
6946 xmlAttrPtr ID;
6948 #ifdef WITH_XSLT_DEBUG_PARSING
6949 xsltGenericDebug(xsltGenericDebugContext,
6950 "xsltLoadStylesheetPI : Reference to ID %s\n", href);
6951 #endif
6952 if (URI->fragment[0] == '#')
6953 ID = xmlGetID(doc, (const xmlChar *) &(URI->fragment[1]));
6954 else
6955 ID = xmlGetID(doc, (const xmlChar *) URI->fragment);
6956 if (ID == NULL) {
6957 xsltTransformError(NULL, NULL, child,
6958 "xml-stylesheet : no ID %s found\n", URI->fragment);
6959 } else {
6960 xmlDocPtr fake;
6961 xmlNodePtr subtree, newtree;
6962 xmlNsPtr ns;
6964 #ifdef WITH_XSLT_DEBUG
6965 xsltGenericDebug(xsltGenericDebugContext,
6966 "creating new document from %s for embedded stylesheet\n",
6967 doc->URL);
6968 #endif
6970 * move the subtree in a new document passed to
6971 * the stylesheet analyzer
6973 subtree = ID->parent;
6974 fake = xmlNewDoc(NULL);
6975 if (fake != NULL) {
6977 * Should the dictionary still be shared even though
6978 * the nodes are being copied rather than moved?
6980 fake->dict = doc->dict;
6981 xmlDictReference(doc->dict);
6982 #ifdef WITH_XSLT_DEBUG
6983 xsltGenericDebug(xsltGenericDebugContext,
6984 "reusing dictionary from %s for embedded stylesheet\n",
6985 doc->URL);
6986 #endif
6988 newtree = xmlDocCopyNode(subtree, fake, 1);
6990 fake->URL = xmlNodeGetBase(doc, subtree->parent);
6991 #ifdef WITH_XSLT_DEBUG
6992 xsltGenericDebug(xsltGenericDebugContext,
6993 "set base URI for embedded stylesheet as %s\n",
6994 fake->URL);
6995 #endif
6998 * Add all namespaces in scope of embedded stylesheet to
6999 * root element of newly created stylesheet document
7001 while ((subtree = subtree->parent) != (xmlNodePtr)doc) {
7002 for (ns = subtree->ns; ns; ns = ns->next) {
7003 xmlNewNs(newtree, ns->href, ns->prefix);
7007 xmlAddChild((xmlNodePtr)fake, newtree);
7008 ret = xsltParseStylesheetDoc(fake);
7009 if (ret == NULL)
7010 xmlFreeDoc(fake);
7013 } else {
7014 xmlChar *URL, *base;
7017 * Reference to an external stylesheet
7020 base = xmlNodeGetBase(doc, (xmlNodePtr) doc);
7021 URL = xmlBuildURI(href, base);
7022 if (URL != NULL) {
7023 #ifdef WITH_XSLT_DEBUG_PARSING
7024 xsltGenericDebug(xsltGenericDebugContext,
7025 "xsltLoadStylesheetPI : fetching %s\n", URL);
7026 #endif
7027 ret = xsltParseStylesheetFile(URL);
7028 xmlFree(URL);
7029 } else {
7030 #ifdef WITH_XSLT_DEBUG_PARSING
7031 xsltGenericDebug(xsltGenericDebugContext,
7032 "xsltLoadStylesheetPI : fetching %s\n", href);
7033 #endif
7034 ret = xsltParseStylesheetFile(href);
7036 if (base != NULL)
7037 xmlFree(base);
7039 xmlFreeURI(URI);
7040 xmlFree(href);
7042 return(ret);