2 * keys.c: Implemetation of the keys support
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
14 #ifdef WITH_XSLT_DEBUG
15 #define WITH_XSLT_DEBUG_KEYS
19 xsltInitDocKeyTable(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
20 const xmlChar
*nameURI
);
22 /************************************************************************
26 ************************************************************************/
30 * @name: the key name or NULL
31 * @nameURI: the name URI or NULL
33 * Create a new XSLT KeyDef
35 * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
38 xsltNewKeyDef(const xmlChar
*name
, const xmlChar
*nameURI
) {
41 cur
= (xsltKeyDefPtr
) xmlMalloc(sizeof(xsltKeyDef
));
43 xsltTransformError(NULL
, NULL
, NULL
,
44 "xsltNewKeyDef : malloc failed\n");
47 memset(cur
, 0, sizeof(xsltKeyDef
));
49 cur
->name
= xmlStrdup(name
);
51 cur
->nameURI
= xmlStrdup(nameURI
);
58 * @keyd: an XSLT key definition
60 * Free up the memory allocated by @keyd
63 xsltFreeKeyDef(xsltKeyDefPtr keyd
) {
66 if (keyd
->comp
!= NULL
)
67 xmlXPathFreeCompExpr(keyd
->comp
);
68 if (keyd
->usecomp
!= NULL
)
69 xmlXPathFreeCompExpr(keyd
->usecomp
);
70 if (keyd
->name
!= NULL
)
72 if (keyd
->nameURI
!= NULL
)
73 xmlFree(keyd
->nameURI
);
74 if (keyd
->match
!= NULL
)
76 if (keyd
->use
!= NULL
)
78 if (keyd
->nsList
!= NULL
)
79 xmlFree(keyd
->nsList
);
80 memset(keyd
, -1, sizeof(xsltKeyDef
));
86 * @keyd: an XSLT key definition list
88 * Free up the memory allocated by all the elements of @keyd
91 xsltFreeKeyDefList(xsltKeyDefPtr keyd
) {
94 while (keyd
!= NULL
) {
103 * @name: the key name or NULL
104 * @nameURI: the name URI or NULL
106 * Create a new XSLT KeyTable
108 * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
110 static xsltKeyTablePtr
111 xsltNewKeyTable(const xmlChar
*name
, const xmlChar
*nameURI
) {
114 cur
= (xsltKeyTablePtr
) xmlMalloc(sizeof(xsltKeyTable
));
116 xsltTransformError(NULL
, NULL
, NULL
,
117 "xsltNewKeyTable : malloc failed\n");
120 memset(cur
, 0, sizeof(xsltKeyTable
));
122 cur
->name
= xmlStrdup(name
);
124 cur
->nameURI
= xmlStrdup(nameURI
);
125 cur
->keys
= xmlHashCreate(0);
131 * @keyt: an XSLT key table
133 * Free up the memory allocated by @keyt
136 xsltFreeKeyTable(xsltKeyTablePtr keyt
) {
139 if (keyt
->name
!= NULL
)
141 if (keyt
->nameURI
!= NULL
)
142 xmlFree(keyt
->nameURI
);
143 if (keyt
->keys
!= NULL
)
144 xmlHashFree(keyt
->keys
,
145 (xmlHashDeallocator
) xmlXPathFreeNodeSet
);
146 memset(keyt
, -1, sizeof(xsltKeyTable
));
151 * xsltFreeKeyTableList:
152 * @keyt: an XSLT key table list
154 * Free up the memory allocated by all the elements of @keyt
157 xsltFreeKeyTableList(xsltKeyTablePtr keyt
) {
160 while (keyt
!= NULL
) {
163 xsltFreeKeyTable(cur
);
167 /************************************************************************
169 * The interpreter for the precompiled patterns *
171 ************************************************************************/
176 * @style: an XSLT stylesheet
178 * Free up the memory used by XSLT keys in a stylesheet
181 xsltFreeKeys(xsltStylesheetPtr style
) {
183 xsltFreeKeyDefList((xsltKeyDefPtr
) style
->keys
);
188 * @cur: the current pointer
189 * @end: the current offset
191 * skip a string delimited by " or '
193 * Returns the byte after the string or -1 in case of error
196 skipString(const xmlChar
*cur
, int end
) {
199 if ((cur
== NULL
) || (end
< 0)) return(-1);
200 if ((cur
[end
] == '\'') || (cur
[end
] == '"')) limit
= cur
[end
];
203 while (cur
[end
] != 0) {
204 if (cur
[end
] == limit
)
213 * @cur: the current pointer
214 * @end: the current offset
218 * Returns the byte after the predicate or -1 in case of error
221 skipPredicate(const xmlChar
*cur
, int end
) {
222 if ((cur
== NULL
) || (end
< 0)) return(-1);
223 if (cur
[end
] != '[') return(end
);
225 while (cur
[end
] != 0) {
226 if ((cur
[end
] == '\'') || (cur
[end
] == '"')) {
227 end
= skipString(cur
, end
);
231 } else if (cur
[end
] == '[') {
232 end
= skipPredicate(cur
, end
);
236 } else if (cur
[end
] == ']')
245 * @style: an XSLT stylesheet
246 * @name: the key name or NULL
247 * @nameURI: the name URI or NULL
248 * @match: the match value
249 * @use: the use value
250 * @inst: the key instruction
252 * add a key definition to a stylesheet
254 * Returns 0 in case of success, and -1 in case of failure.
257 xsltAddKey(xsltStylesheetPtr style
, const xmlChar
*name
,
258 const xmlChar
*nameURI
, const xmlChar
*match
,
259 const xmlChar
*use
, xmlNodePtr inst
) {
261 xmlChar
*pattern
= NULL
;
262 int current
, end
, start
, i
= 0;
264 if ((style
== NULL
) || (name
== NULL
) || (match
== NULL
) || (use
== NULL
))
267 #ifdef WITH_XSLT_DEBUG_KEYS
268 xsltGenericDebug(xsltGenericDebugContext
,
269 "Add key %s, match %s, use %s\n", name
, match
, use
);
272 key
= xsltNewKeyDef(name
, nameURI
);
273 key
->match
= xmlStrdup(match
);
274 key
->use
= xmlStrdup(use
);
276 key
->nsList
= xmlGetNsList(inst
->doc
, inst
);
277 if (key
->nsList
!= NULL
) {
278 while (key
->nsList
[i
] != NULL
)
284 * Split the | and register it as as many keys
287 while (match
[current
] != 0) {
289 while (IS_BLANK_CH(match
[current
]))
292 while ((match
[end
] != 0) && (match
[end
] != '|')) {
293 if (match
[end
] == '[') {
294 end
= skipPredicate(match
, end
);
296 xsltTransformError(NULL
, style
, inst
,
297 "xsl:key : 'match' pattern is malformed: %s",
299 if (style
!= NULL
) style
->errors
++;
305 if (current
== end
) {
306 xsltTransformError(NULL
, style
, inst
,
307 "xsl:key : 'match' pattern is empty\n");
308 if (style
!= NULL
) style
->errors
++;
311 if (match
[start
] != '/') {
312 pattern
= xmlStrcat(pattern
, (xmlChar
*)"//");
313 if (pattern
== NULL
) {
314 if (style
!= NULL
) style
->errors
++;
318 pattern
= xmlStrncat(pattern
, &match
[start
], end
- start
);
319 if (pattern
== NULL
) {
320 if (style
!= NULL
) style
->errors
++;
324 if (match
[end
] == '|') {
325 pattern
= xmlStrcat(pattern
, (xmlChar
*)"|");
330 if (pattern
== NULL
) {
331 xsltTransformError(NULL
, style
, inst
,
332 "xsl:key : 'match' pattern is empty\n");
333 if (style
!= NULL
) style
->errors
++;
336 #ifdef WITH_XSLT_DEBUG_KEYS
337 xsltGenericDebug(xsltGenericDebugContext
,
338 " resulting pattern %s\n", pattern
);
341 * XSLT-1: "It is an error for the value of either the use
342 * attribute or the match attribute to contain a
343 * VariableReference."
344 * TODO: We should report a variable-reference at compile-time.
345 * Maybe a search for "$", if it occurs outside of quotation
346 * marks, could be sufficient.
348 #ifdef XML_XPATH_NOVAR
349 key
->comp
= xsltXPathCompileFlags(style
, pattern
, XML_XPATH_NOVAR
);
351 key
->comp
= xsltXPathCompile(style
, pattern
);
353 if (key
->comp
== NULL
) {
354 xsltTransformError(NULL
, style
, inst
,
355 "xsl:key : 'match' pattern compilation failed '%s'\n",
357 if (style
!= NULL
) style
->errors
++;
359 #ifdef XML_XPATH_NOVAR
360 key
->usecomp
= xsltXPathCompileFlags(style
, use
, XML_XPATH_NOVAR
);
362 key
->usecomp
= xsltXPathCompile(style
, use
);
364 if (key
->usecomp
== NULL
) {
365 xsltTransformError(NULL
, style
, inst
,
366 "xsl:key : 'use' expression compilation failed '%s'\n",
368 if (style
!= NULL
) style
->errors
++;
372 * Sometimes the stylesheet writer use the order to ease the
373 * resolution of keys when they are dependant, keep the provided
374 * order so add the new one at the end.
376 if (style
->keys
== NULL
) {
379 xsltKeyDefPtr prev
= style
->keys
;
381 while (prev
->next
!= NULL
)
396 * @ctxt: an XSLT transformation context
397 * @name: the key name or NULL
398 * @nameURI: the name URI or NULL
399 * @value: the key value to look for
401 * Looks up a key of the in current source doc (the document info
402 * on @ctxt->document). Computes the key if not already done
403 * for the current source doc.
405 * Returns the nodeset resulting from the query or NULL
408 xsltGetKey(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
409 const xmlChar
*nameURI
, const xmlChar
*value
) {
411 xsltKeyTablePtr table
;
414 if ((ctxt
== NULL
) || (name
== NULL
) || (value
== NULL
) ||
415 (ctxt
->document
== NULL
))
418 #ifdef WITH_XSLT_DEBUG_KEYS
419 xsltGenericDebug(xsltGenericDebugContext
,
420 "Get key %s, value %s\n", name
, value
);
424 * keys are computed only on-demand on first key access for a document
426 if ((ctxt
->document
->nbKeysComputed
< ctxt
->nbKeys
) &&
427 (ctxt
->keyInitLevel
== 0)) {
429 * If non-recursive behaviour, just try to initialize all keys
431 if (xsltInitAllDocKeys(ctxt
))
436 table
= (xsltKeyTablePtr
) ctxt
->document
->keys
;
437 while (table
!= NULL
) {
438 if (((nameURI
!= NULL
) == (table
->nameURI
!= NULL
)) &&
439 xmlStrEqual(table
->name
, name
) &&
440 xmlStrEqual(table
->nameURI
, nameURI
))
442 ret
= (xmlNodeSetPtr
)xmlHashLookup(table
->keys
, value
);
448 if ((ctxt
->keyInitLevel
!= 0) && (init_table
== 0)) {
450 * Apparently one key is recursive and this one is needed,
451 * initialize just it, that time and retry
453 xsltInitDocKeyTable(ctxt
, name
, nameURI
);
463 * xsltInitDocKeyTable:
465 * INTERNAL ROUTINE ONLY
467 * Check if any keys on the current document need to be computed
470 xsltInitDocKeyTable(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
471 const xmlChar
*nameURI
)
473 xsltStylesheetPtr style
;
474 xsltKeyDefPtr keyd
= NULL
;
477 #ifdef KEY_INIT_DEBUG
478 fprintf(stderr
, "xsltInitDocKeyTable %s\n", name
);
482 while (style
!= NULL
) {
483 keyd
= (xsltKeyDefPtr
) style
->keys
;
484 while (keyd
!= NULL
) {
485 if (((keyd
->nameURI
!= NULL
) ==
486 (nameURI
!= NULL
)) &&
487 xmlStrEqual(keyd
->name
, name
) &&
488 xmlStrEqual(keyd
->nameURI
, nameURI
))
490 xsltInitCtxtKey(ctxt
, ctxt
->document
, keyd
);
491 if (ctxt
->document
->nbKeysComputed
== ctxt
->nbKeys
)
497 style
= xsltNextImport(style
);
500 #ifdef WITH_XSLT_DEBUG_KEYS
501 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
502 "xsltInitDocKeyTable: did not found %s\n", name
));
504 xsltTransformError(ctxt
, NULL
, keyd
? keyd
->inst
: NULL
,
505 "Failed to find key definition for %s\n", name
);
506 ctxt
->state
= XSLT_STATE_STOPPED
;
509 #ifdef KEY_INIT_DEBUG
510 fprintf(stderr
, "xsltInitDocKeyTable %s done\n", name
);
516 * xsltInitAllDocKeys:
517 * @ctxt: transformation context
519 * INTERNAL ROUTINE ONLY
521 * Check if any keys on the current document need to be computed
523 * Returns 0 in case of success, -1 in case of failure
526 xsltInitAllDocKeys(xsltTransformContextPtr ctxt
)
528 xsltStylesheetPtr style
;
530 xsltKeyTablePtr table
;
535 #ifdef KEY_INIT_DEBUG
536 fprintf(stderr
, "xsltInitAllDocKeys %d %d\n",
537 ctxt
->document
->nbKeysComputed
, ctxt
->nbKeys
);
540 if (ctxt
->document
->nbKeysComputed
== ctxt
->nbKeys
)
545 * TODO: This could be further optimized
549 keyd
= (xsltKeyDefPtr
) style
->keys
;
550 while (keyd
!= NULL
) {
551 #ifdef KEY_INIT_DEBUG
552 fprintf(stderr
, "Init key %s\n", keyd
->name
);
555 * Check if keys with this QName have been already
558 table
= (xsltKeyTablePtr
) ctxt
->document
->keys
;
560 if (((keyd
->nameURI
!= NULL
) == (table
->nameURI
!= NULL
)) &&
561 xmlStrEqual(keyd
->name
, table
->name
) &&
562 xmlStrEqual(keyd
->nameURI
, table
->nameURI
))
570 * Keys with this QName have not been yet computed.
572 xsltInitDocKeyTable(ctxt
, keyd
->name
, keyd
->nameURI
);
576 style
= xsltNextImport(style
);
578 #ifdef KEY_INIT_DEBUG
579 fprintf(stderr
, "xsltInitAllDocKeys: done\n");
586 * @ctxt: an XSLT transformation context
587 * @idoc: the document information (holds key values)
588 * @keyDef: the key definition
590 * Computes the key tables this key and for the current input document.
592 * Returns: 0 on success, -1 on error
595 xsltInitCtxtKey(xsltTransformContextPtr ctxt
, xsltDocumentPtr idoc
,
596 xsltKeyDefPtr keyDef
)
599 xmlNodeSetPtr matchList
= NULL
, keylist
;
600 xmlXPathObjectPtr matchRes
= NULL
, useRes
= NULL
;
602 xsltKeyTablePtr table
;
603 xmlNodePtr oldInst
, cur
;
604 xmlNodePtr oldContextNode
;
605 xsltDocumentPtr oldDocInfo
;
606 int oldXPPos
, oldXPSize
;
609 xmlNsPtr
*oldXPNamespaces
;
610 xmlXPathContextPtr xpctxt
;
612 #ifdef KEY_INIT_DEBUG
613 fprintf(stderr
, "xsltInitCtxtKey %s : %d\n", keyDef
->name
, ctxt
->keyInitLevel
);
616 if ((keyDef
->comp
== NULL
) || (keyDef
->usecomp
== NULL
))
620 * Detect recursive keys
622 if (ctxt
->keyInitLevel
> ctxt
->nbKeys
) {
623 #ifdef WITH_XSLT_DEBUG_KEYS
624 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,
625 xsltGenericDebug(xsltGenericDebugContext
,
626 "xsltInitCtxtKey: key definition of %s is recursive\n",
629 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
630 "Key definition for %s is recursive\n", keyDef
->name
);
631 ctxt
->state
= XSLT_STATE_STOPPED
;
634 ctxt
->keyInitLevel
++;
636 xpctxt
= ctxt
->xpathCtxt
;
637 idoc
->nbKeysComputed
++;
639 * Save context state.
641 oldInst
= ctxt
->inst
;
642 oldDocInfo
= ctxt
->document
;
643 oldContextNode
= ctxt
->node
;
645 oldXPDoc
= xpctxt
->doc
;
646 oldXPPos
= xpctxt
->proximityPosition
;
647 oldXPSize
= xpctxt
->contextSize
;
648 oldXPNsNr
= xpctxt
->nsNr
;
649 oldXPNamespaces
= xpctxt
->namespaces
;
654 ctxt
->document
= idoc
;
655 ctxt
->node
= (xmlNodePtr
) idoc
->doc
;
656 ctxt
->inst
= keyDef
->inst
;
658 xpctxt
->doc
= idoc
->doc
;
659 xpctxt
->node
= (xmlNodePtr
) idoc
->doc
;
660 /* TODO : clarify the use of namespaces in keys evaluation */
661 xpctxt
->namespaces
= keyDef
->nsList
;
662 xpctxt
->nsNr
= keyDef
->nsNr
;
665 * Evaluate the 'match' expression of the xsl:key.
666 * TODO: The 'match' is a *pattern*.
668 matchRes
= xmlXPathCompiledEval(keyDef
->comp
, xpctxt
);
669 if (matchRes
== NULL
) {
671 #ifdef WITH_XSLT_DEBUG_KEYS
672 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
673 "xsltInitCtxtKey: %s evaluation failed\n", keyDef
->match
));
675 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
676 "Failed to evaluate the 'match' expression.\n");
677 ctxt
->state
= XSLT_STATE_STOPPED
;
680 if (matchRes
->type
== XPATH_NODESET
) {
681 matchList
= matchRes
->nodesetval
;
683 #ifdef WITH_XSLT_DEBUG_KEYS
684 if (matchList
!= NULL
)
685 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
686 "xsltInitCtxtKey: %s evaluates to %d nodes\n",
687 keyDef
->match
, matchList
->nodeNr
));
691 * Is not a node set, but must be.
693 #ifdef WITH_XSLT_DEBUG_KEYS
694 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
695 "xsltInitCtxtKey: %s is not a node set\n", keyDef
->match
));
697 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
698 "The 'match' expression did not evaluate to a node set.\n");
699 ctxt
->state
= XSLT_STATE_STOPPED
;
703 if ((matchList
== NULL
) || (matchList
->nodeNr
<= 0))
707 * Multiple key definitions for the same name are allowed, so
708 * we must check if the key is already present for this doc
710 table
= (xsltKeyTablePtr
) idoc
->keys
;
711 while (table
!= NULL
) {
712 if (xmlStrEqual(table
->name
, keyDef
->name
) &&
713 (((keyDef
->nameURI
== NULL
) && (table
->nameURI
== NULL
)) ||
714 ((keyDef
->nameURI
!= NULL
) && (table
->nameURI
!= NULL
) &&
715 (xmlStrEqual(table
->nameURI
, keyDef
->nameURI
)))))
720 * If the key was not previously defined, create it now and
721 * chain it to the list of keys for the doc
724 table
= xsltNewKeyTable(keyDef
->name
, keyDef
->nameURI
);
727 table
->next
= idoc
->keys
;
732 * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
733 * "...the use attribute of the xsl:key element is evaluated with x as
734 " the current node and with a node list containing just x as the
737 xpctxt
->contextSize
= 1;
738 xpctxt
->proximityPosition
= 1;
740 for (i
= 0; i
< matchList
->nodeNr
; i
++) {
741 cur
= matchList
->nodeTab
[i
];
742 if (! IS_XSLT_REAL_NODE(cur
))
746 * Process the 'use' of the xsl:key.
748 * "The use attribute is an expression specifying the values of
749 * the key; the expression is evaluated once for each node that
750 * matches the pattern."
753 xmlXPathFreeObject(useRes
);
754 useRes
= xmlXPathCompiledEval(keyDef
->usecomp
, xpctxt
);
755 if (useRes
== NULL
) {
756 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
757 "Failed to evaluate the 'use' expression.\n");
758 ctxt
->state
= XSLT_STATE_STOPPED
;
761 if (useRes
->type
== XPATH_NODESET
) {
762 if ((useRes
->nodesetval
!= NULL
) &&
763 (useRes
->nodesetval
->nodeNr
!= 0))
765 len
= useRes
->nodesetval
->nodeNr
;
766 str
= xmlXPathCastNodeToString(useRes
->nodesetval
->nodeTab
[0]);
772 if (useRes
->type
== XPATH_STRING
) {
774 * Consume the string value.
776 str
= useRes
->stringval
;
777 useRes
->stringval
= NULL
;
779 str
= xmlXPathCastToString(useRes
);
783 * Process all strings.
790 #ifdef WITH_XSLT_DEBUG_KEYS
791 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
792 "xsl:key : node associated to ('%s', '%s')\n", keyDef
->name
, str
));
795 keylist
= xmlHashLookup(table
->keys
, str
);
796 if (keylist
== NULL
) {
797 keylist
= xmlXPathNodeSetCreate(cur
);
800 xmlHashAddEntry(table
->keys
, str
, keylist
);
803 * TODO: How do we know if this function failed?
805 xmlXPathNodeSetAdd(keylist
, cur
);
808 case XML_ELEMENT_NODE
:
810 case XML_CDATA_SECTION_NODE
:
812 case XML_COMMENT_NODE
:
815 case XML_ATTRIBUTE_NODE
:
816 ((xmlAttrPtr
) cur
)->psvi
= keyDef
;
818 case XML_DOCUMENT_NODE
:
819 case XML_HTML_DOCUMENT_NODE
:
820 ((xmlDocPtr
) cur
)->psvi
= keyDef
;
832 str
= xmlXPathCastNodeToString(useRes
->nodesetval
->nodeTab
[k
]);
838 ctxt
->keyInitLevel
--;
840 * Restore context state.
842 xpctxt
->doc
= oldXPDoc
;
843 xpctxt
->nsNr
= oldXPNsNr
;
844 xpctxt
->namespaces
= oldXPNamespaces
;
845 xpctxt
->proximityPosition
= oldXPPos
;
846 xpctxt
->contextSize
= oldXPSize
;
848 ctxt
->node
= oldContextNode
;
849 ctxt
->document
= oldDocInfo
;
850 ctxt
->inst
= oldInst
;
855 xmlXPathFreeObject(useRes
);
856 if (matchRes
!= NULL
)
857 xmlXPathFreeObject(matchRes
);
863 * @ctxt: an XSLT transformation context
864 * @idoc: a document info
866 * Computes all the keys tables for the current input document.
867 * Should be done before global varibales are initialized.
868 * NOTE: Not used anymore in the refactored code.
871 xsltInitCtxtKeys(xsltTransformContextPtr ctxt
, xsltDocumentPtr idoc
) {
872 xsltStylesheetPtr style
;
873 xsltKeyDefPtr keyDef
;
875 if ((ctxt
== NULL
) || (idoc
== NULL
))
878 #ifdef KEY_INIT_DEBUG
879 fprintf(stderr
, "xsltInitCtxtKeys on document\n");
882 #ifdef WITH_XSLT_DEBUG_KEYS
883 if ((idoc
->doc
!= NULL
) && (idoc
->doc
->URL
!= NULL
))
884 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
, "Initializing keys on %s\n",
888 while (style
!= NULL
) {
889 keyDef
= (xsltKeyDefPtr
) style
->keys
;
890 while (keyDef
!= NULL
) {
891 xsltInitCtxtKey(ctxt
, idoc
, keyDef
);
893 keyDef
= keyDef
->next
;
896 style
= xsltNextImport(style
);
899 #ifdef KEY_INIT_DEBUG
900 fprintf(stderr
, "xsltInitCtxtKeys on document: done\n");
906 * xsltFreeDocumentKeys:
907 * @idoc: a XSLT document
909 * Free the keys associated to a document
912 xsltFreeDocumentKeys(xsltDocumentPtr idoc
) {
914 xsltFreeKeyTableList(idoc
->keys
);