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.
17 #include <libxml/xmlmemory.h>
18 #include <libxml/tree.h>
19 #include <libxml/valid.h>
20 #include <libxml/hash.h>
21 #include <libxml/xmlerror.h>
22 #include <libxml/parserInternals.h>
23 #include <libxml/xpathInternals.h>
25 #include "xsltInternals.h"
26 #include "xsltutils.h"
28 #include "templates.h"
31 #ifdef WITH_XSLT_DEBUG
32 #define WITH_XSLT_DEBUG_KEYS
36 xsltInitDocKeyTable(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
37 const xmlChar
*nameURI
);
39 /************************************************************************
43 ************************************************************************/
47 * @name: the key name or NULL
48 * @nameURI: the name URI or NULL
50 * Create a new XSLT KeyDef
52 * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
55 xsltNewKeyDef(const xmlChar
*name
, const xmlChar
*nameURI
) {
58 cur
= (xsltKeyDefPtr
) xmlMalloc(sizeof(xsltKeyDef
));
60 xsltTransformError(NULL
, NULL
, NULL
,
61 "xsltNewKeyDef : malloc failed\n");
64 memset(cur
, 0, sizeof(xsltKeyDef
));
66 cur
->name
= xmlStrdup(name
);
68 cur
->nameURI
= xmlStrdup(nameURI
);
75 * @keyd: an XSLT key definition
77 * Free up the memory allocated by @keyd
80 xsltFreeKeyDef(xsltKeyDefPtr keyd
) {
83 if (keyd
->comp
!= NULL
)
84 xmlXPathFreeCompExpr(keyd
->comp
);
85 if (keyd
->usecomp
!= NULL
)
86 xmlXPathFreeCompExpr(keyd
->usecomp
);
87 if (keyd
->name
!= NULL
)
89 if (keyd
->nameURI
!= NULL
)
90 xmlFree(keyd
->nameURI
);
91 if (keyd
->match
!= NULL
)
93 if (keyd
->use
!= NULL
)
95 if (keyd
->nsList
!= NULL
)
96 xmlFree(keyd
->nsList
);
97 memset(keyd
, -1, sizeof(xsltKeyDef
));
102 * xsltFreeKeyDefList:
103 * @keyd: an XSLT key definition list
105 * Free up the memory allocated by all the elements of @keyd
108 xsltFreeKeyDefList(xsltKeyDefPtr keyd
) {
111 while (keyd
!= NULL
) {
120 * @name: the key name or NULL
121 * @nameURI: the name URI or NULL
123 * Create a new XSLT KeyTable
125 * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
127 static xsltKeyTablePtr
128 xsltNewKeyTable(const xmlChar
*name
, const xmlChar
*nameURI
) {
131 cur
= (xsltKeyTablePtr
) xmlMalloc(sizeof(xsltKeyTable
));
133 xsltTransformError(NULL
, NULL
, NULL
,
134 "xsltNewKeyTable : malloc failed\n");
137 memset(cur
, 0, sizeof(xsltKeyTable
));
139 cur
->name
= xmlStrdup(name
);
141 cur
->nameURI
= xmlStrdup(nameURI
);
142 cur
->keys
= xmlHashCreate(0);
148 * @keyt: an XSLT key table
150 * Free up the memory allocated by @keyt
153 xsltFreeKeyTable(xsltKeyTablePtr keyt
) {
156 if (keyt
->name
!= NULL
)
158 if (keyt
->nameURI
!= NULL
)
159 xmlFree(keyt
->nameURI
);
160 if (keyt
->keys
!= NULL
)
161 xmlHashFree(keyt
->keys
,
162 (xmlHashDeallocator
) xmlXPathFreeNodeSet
);
163 memset(keyt
, -1, sizeof(xsltKeyTable
));
168 * xsltFreeKeyTableList:
169 * @keyt: an XSLT key table list
171 * Free up the memory allocated by all the elements of @keyt
174 xsltFreeKeyTableList(xsltKeyTablePtr keyt
) {
177 while (keyt
!= NULL
) {
180 xsltFreeKeyTable(cur
);
184 /************************************************************************
186 * The interpreter for the precompiled patterns *
188 ************************************************************************/
193 * @style: an XSLT stylesheet
195 * Free up the memory used by XSLT keys in a stylesheet
198 xsltFreeKeys(xsltStylesheetPtr style
) {
200 xsltFreeKeyDefList((xsltKeyDefPtr
) style
->keys
);
205 * @cur: the current pointer
206 * @end: the current offset
208 * skip a string delimited by " or '
210 * Returns the byte after the string or -1 in case of error
213 skipString(const xmlChar
*cur
, int end
) {
216 if ((cur
== NULL
) || (end
< 0)) return(-1);
217 if ((cur
[end
] == '\'') || (cur
[end
] == '"')) limit
= cur
[end
];
220 while (cur
[end
] != 0) {
221 if (cur
[end
] == limit
)
230 * @cur: the current pointer
231 * @end: the current offset
235 * Returns the byte after the predicate or -1 in case of error
238 skipPredicate(const xmlChar
*cur
, int end
) {
239 if ((cur
== NULL
) || (end
< 0)) return(-1);
240 if (cur
[end
] != '[') return(end
);
242 while (cur
[end
] != 0) {
243 if ((cur
[end
] == '\'') || (cur
[end
] == '"')) {
244 end
= skipString(cur
, end
);
248 } else if (cur
[end
] == '[') {
249 end
= skipPredicate(cur
, end
);
253 } else if (cur
[end
] == ']')
262 * @style: an XSLT stylesheet
263 * @name: the key name or NULL
264 * @nameURI: the name URI or NULL
265 * @match: the match value
266 * @use: the use value
267 * @inst: the key instruction
269 * add a key definition to a stylesheet
271 * Returns 0 in case of success, and -1 in case of failure.
274 xsltAddKey(xsltStylesheetPtr style
, const xmlChar
*name
,
275 const xmlChar
*nameURI
, const xmlChar
*match
,
276 const xmlChar
*use
, xmlNodePtr inst
) {
278 xmlChar
*pattern
= NULL
;
279 int current
, end
, start
, i
= 0;
281 if ((style
== NULL
) || (name
== NULL
) || (match
== NULL
) || (use
== NULL
))
284 #ifdef WITH_XSLT_DEBUG_KEYS
285 xsltGenericDebug(xsltGenericDebugContext
,
286 "Add key %s, match %s, use %s\n", name
, match
, use
);
289 key
= xsltNewKeyDef(name
, nameURI
);
290 key
->match
= xmlStrdup(match
);
291 key
->use
= xmlStrdup(use
);
293 key
->nsList
= xmlGetNsList(inst
->doc
, inst
);
294 if (key
->nsList
!= NULL
) {
295 while (key
->nsList
[i
] != NULL
)
301 * Split the | and register it as as many keys
304 while (match
[current
] != 0) {
306 while (IS_BLANK_CH(match
[current
]))
309 while ((match
[end
] != 0) && (match
[end
] != '|')) {
310 if (match
[end
] == '[') {
311 end
= skipPredicate(match
, end
);
313 xsltTransformError(NULL
, style
, inst
,
314 "key pattern is malformed: %s",
316 if (style
!= NULL
) style
->errors
++;
322 if (current
== end
) {
323 xsltTransformError(NULL
, style
, inst
,
324 "key pattern is empty\n");
325 if (style
!= NULL
) style
->errors
++;
328 if (match
[start
] != '/') {
329 pattern
= xmlStrcat(pattern
, (xmlChar
*)"//");
330 if (pattern
== NULL
) {
331 if (style
!= NULL
) style
->errors
++;
335 pattern
= xmlStrncat(pattern
, &match
[start
], end
- start
);
336 if (pattern
== NULL
) {
337 if (style
!= NULL
) style
->errors
++;
341 if (match
[end
] == '|') {
342 pattern
= xmlStrcat(pattern
, (xmlChar
*)"|");
347 #ifdef WITH_XSLT_DEBUG_KEYS
348 xsltGenericDebug(xsltGenericDebugContext
,
349 " resulting pattern %s\n", pattern
);
352 * XSLT-1: "It is an error for the value of either the use
353 * attribute or the match attribute to contain a
354 * VariableReference."
355 * TODO: We should report a variable-reference at compile-time.
356 * Maybe a search for "$", if it occurs outside of quotation
357 * marks, could be sufficient.
359 key
->comp
= xsltXPathCompile(style
, pattern
);
360 if (key
->comp
== NULL
) {
361 xsltTransformError(NULL
, style
, inst
,
362 "xsl:key : XPath pattern compilation failed '%s'\n",
364 if (style
!= NULL
) style
->errors
++;
366 key
->usecomp
= xsltXPathCompile(style
, use
);
367 if (key
->usecomp
== NULL
) {
368 xsltTransformError(NULL
, style
, inst
,
369 "xsl:key : XPath pattern compilation failed '%s'\n",
371 if (style
!= NULL
) style
->errors
++;
375 * Sometimes the stylesheet writer use the order to ease the
376 * resolution of keys when they are dependant, keep the provided
377 * order so add the new one at the end.
379 if (style
->keys
== NULL
) {
382 xsltKeyDefPtr prev
= style
->keys
;
384 while (prev
->next
!= NULL
)
399 * @ctxt: an XSLT transformation context
400 * @name: the key name or NULL
401 * @nameURI: the name URI or NULL
402 * @value: the key value to look for
404 * Looks up a key of the in current source doc (the document info
405 * on @ctxt->document). Computes the key if not already done
406 * for the current source doc.
408 * Returns the nodeset resulting from the query or NULL
411 xsltGetKey(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
412 const xmlChar
*nameURI
, const xmlChar
*value
) {
414 xsltKeyTablePtr table
;
417 if ((ctxt
== NULL
) || (name
== NULL
) || (value
== NULL
) ||
418 (ctxt
->document
== NULL
))
421 #ifdef WITH_XSLT_DEBUG_KEYS
422 xsltGenericDebug(xsltGenericDebugContext
,
423 "Get key %s, value %s\n", name
, value
);
427 * keys are computed only on-demand on first key access for a document
429 if ((ctxt
->document
->nbKeysComputed
< ctxt
->nbKeys
) &&
430 (ctxt
->keyInitLevel
== 0)) {
432 * If non-recursive behaviour, just try to initialize all keys
434 if (xsltInitAllDocKeys(ctxt
))
439 table
= (xsltKeyTablePtr
) ctxt
->document
->keys
;
440 while (table
!= NULL
) {
441 if (((nameURI
!= NULL
) == (table
->nameURI
!= NULL
)) &&
442 xmlStrEqual(table
->name
, name
) &&
443 xmlStrEqual(table
->nameURI
, nameURI
))
445 ret
= (xmlNodeSetPtr
)xmlHashLookup(table
->keys
, value
);
451 if ((ctxt
->keyInitLevel
!= 0) && (init_table
== 0)) {
453 * Apparently one key is recursive and this one is needed,
454 * initialize just it, that time and retry
456 xsltInitDocKeyTable(ctxt
, name
, nameURI
);
466 * xsltInitDocKeyTable:
468 * INTERNAL ROUTINE ONLY
470 * Check if any keys on the current document need to be computed
473 xsltInitDocKeyTable(xsltTransformContextPtr ctxt
, const xmlChar
*name
,
474 const xmlChar
*nameURI
)
476 xsltStylesheetPtr style
;
477 xsltKeyDefPtr keyd
= NULL
;
480 #ifdef KEY_INIT_DEBUG
481 fprintf(stderr
, "xsltInitDocKeyTable %s\n", name
);
485 while (style
!= NULL
) {
486 keyd
= (xsltKeyDefPtr
) style
->keys
;
487 while (keyd
!= NULL
) {
488 if (((keyd
->nameURI
!= NULL
) ==
489 (nameURI
!= NULL
)) &&
490 xmlStrEqual(keyd
->name
, name
) &&
491 xmlStrEqual(keyd
->nameURI
, nameURI
))
493 xsltInitCtxtKey(ctxt
, ctxt
->document
, keyd
);
494 if (ctxt
->document
->nbKeysComputed
== ctxt
->nbKeys
)
500 style
= xsltNextImport(style
);
503 #ifdef WITH_XSLT_DEBUG_KEYS
504 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
505 "xsltInitDocKeyTable: did not found %s\n", name
));
507 xsltTransformError(ctxt
, NULL
, keyd
? keyd
->inst
: NULL
,
508 "Failed to find key definition for %s\n", name
);
509 ctxt
->state
= XSLT_STATE_STOPPED
;
512 #ifdef KEY_INIT_DEBUG
513 fprintf(stderr
, "xsltInitDocKeyTable %s done\n", name
);
519 * xsltInitAllDocKeys:
520 * @ctxt: transformation context
522 * INTERNAL ROUTINE ONLY
524 * Check if any keys on the current document need to be computed
526 * Returns 0 in case of success, -1 in case of failure
529 xsltInitAllDocKeys(xsltTransformContextPtr ctxt
)
531 xsltStylesheetPtr style
;
533 xsltKeyTablePtr table
;
538 #ifdef KEY_INIT_DEBUG
539 fprintf(stderr
, "xsltInitAllDocKeys %d %d\n",
540 ctxt
->document
->nbKeysComputed
, ctxt
->nbKeys
);
543 if (ctxt
->document
->nbKeysComputed
== ctxt
->nbKeys
)
548 * TODO: This could be further optimized
552 keyd
= (xsltKeyDefPtr
) style
->keys
;
553 while (keyd
!= NULL
) {
554 #ifdef KEY_INIT_DEBUG
555 fprintf(stderr
, "Init key %s\n", keyd
->name
);
558 * Check if keys with this QName have been already
561 table
= (xsltKeyTablePtr
) ctxt
->document
->keys
;
563 if (((keyd
->nameURI
!= NULL
) == (table
->nameURI
!= NULL
)) &&
564 xmlStrEqual(keyd
->name
, table
->name
) &&
565 xmlStrEqual(keyd
->nameURI
, table
->nameURI
))
573 * Keys with this QName have not been yet computed.
575 xsltInitDocKeyTable(ctxt
, keyd
->name
, keyd
->nameURI
);
579 style
= xsltNextImport(style
);
581 #ifdef KEY_INIT_DEBUG
582 fprintf(stderr
, "xsltInitAllDocKeys: done\n");
589 * @ctxt: an XSLT transformation context
590 * @idoc: the document information (holds key values)
591 * @keyDef: the key definition
593 * Computes the key tables this key and for the current input document.
595 * Returns: 0 on success, -1 on error
598 xsltInitCtxtKey(xsltTransformContextPtr ctxt
, xsltDocumentPtr idoc
,
599 xsltKeyDefPtr keyDef
)
602 xmlNodeSetPtr matchList
= NULL
, keylist
;
603 xmlXPathObjectPtr matchRes
= NULL
, useRes
= NULL
;
605 xsltKeyTablePtr table
;
606 xmlNodePtr oldInst
, cur
;
607 xmlNodePtr oldContextNode
;
608 xsltDocumentPtr oldDocInfo
;
609 int oldXPPos
, oldXPSize
;
612 xmlNsPtr
*oldXPNamespaces
;
613 xmlXPathContextPtr xpctxt
;
615 #ifdef KEY_INIT_DEBUG
616 fprintf(stderr
, "xsltInitCtxtKey %s : %d\n", keyDef
->name
, ctxt
->keyInitLevel
);
619 if ((keyDef
->comp
== NULL
) || (keyDef
->usecomp
== NULL
))
623 * Detect recursive keys
625 if (ctxt
->keyInitLevel
> ctxt
->nbKeys
) {
626 #ifdef WITH_XSLT_DEBUG_KEYS
627 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,
628 xsltGenericDebug(xsltGenericDebugContext
,
629 "xsltInitCtxtKey: key definition of %s is recursive\n",
632 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
633 "Key definition for %s is recursive\n", keyDef
->name
);
634 ctxt
->state
= XSLT_STATE_STOPPED
;
637 ctxt
->keyInitLevel
++;
639 xpctxt
= ctxt
->xpathCtxt
;
640 idoc
->nbKeysComputed
++;
642 * Save context state.
644 oldInst
= ctxt
->inst
;
645 oldDocInfo
= ctxt
->document
;
646 oldContextNode
= ctxt
->node
;
648 oldXPDoc
= xpctxt
->doc
;
649 oldXPPos
= xpctxt
->proximityPosition
;
650 oldXPSize
= xpctxt
->contextSize
;
651 oldXPNsNr
= xpctxt
->nsNr
;
652 oldXPNamespaces
= xpctxt
->namespaces
;
657 ctxt
->document
= idoc
;
658 ctxt
->node
= (xmlNodePtr
) idoc
->doc
;
659 ctxt
->inst
= keyDef
->inst
;
661 xpctxt
->doc
= idoc
->doc
;
662 xpctxt
->node
= (xmlNodePtr
) idoc
->doc
;
663 /* TODO : clarify the use of namespaces in keys evaluation */
664 xpctxt
->namespaces
= keyDef
->nsList
;
665 xpctxt
->nsNr
= keyDef
->nsNr
;
668 * Evaluate the 'match' expression of the xsl:key.
669 * TODO: The 'match' is a *pattern*.
671 matchRes
= xmlXPathCompiledEval(keyDef
->comp
, xpctxt
);
672 if (matchRes
== NULL
) {
674 #ifdef WITH_XSLT_DEBUG_KEYS
675 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
676 "xsltInitCtxtKey: %s evaluation failed\n", keyDef
->match
));
678 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
679 "Failed to evaluate the 'match' expression.\n");
680 ctxt
->state
= XSLT_STATE_STOPPED
;
683 if (matchRes
->type
== XPATH_NODESET
) {
684 matchList
= matchRes
->nodesetval
;
686 #ifdef WITH_XSLT_DEBUG_KEYS
687 if (matchList
!= NULL
)
688 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
689 "xsltInitCtxtKey: %s evaluates to %d nodes\n",
690 keyDef
->match
, matchList
->nodeNr
));
694 * Is not a node set, but must be.
696 #ifdef WITH_XSLT_DEBUG_KEYS
697 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
698 "xsltInitCtxtKey: %s is not a node set\n", keyDef
->match
));
700 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
701 "The 'match' expression did not evaluate to a node set.\n");
702 ctxt
->state
= XSLT_STATE_STOPPED
;
706 if ((matchList
== NULL
) || (matchList
->nodeNr
<= 0))
710 * Multiple key definitions for the same name are allowed, so
711 * we must check if the key is already present for this doc
713 table
= (xsltKeyTablePtr
) idoc
->keys
;
714 while (table
!= NULL
) {
715 if (xmlStrEqual(table
->name
, keyDef
->name
) &&
716 (((keyDef
->nameURI
== NULL
) && (table
->nameURI
== NULL
)) ||
717 ((keyDef
->nameURI
!= NULL
) && (table
->nameURI
!= NULL
) &&
718 (xmlStrEqual(table
->nameURI
, keyDef
->nameURI
)))))
723 * If the key was not previously defined, create it now and
724 * chain it to the list of keys for the doc
727 table
= xsltNewKeyTable(keyDef
->name
, keyDef
->nameURI
);
730 table
->next
= idoc
->keys
;
735 * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
736 * "...the use attribute of the xsl:key element is evaluated with x as
737 " the current node and with a node list containing just x as the
740 xpctxt
->contextSize
= 1;
741 xpctxt
->proximityPosition
= 1;
743 for (i
= 0; i
< matchList
->nodeNr
; i
++) {
744 cur
= matchList
->nodeTab
[i
];
745 if (! IS_XSLT_REAL_NODE(cur
))
749 * Process the 'use' of the xsl:key.
751 * "The use attribute is an expression specifying the values of
752 * the key; the expression is evaluated once for each node that
753 * matches the pattern."
756 xmlXPathFreeObject(useRes
);
757 useRes
= xmlXPathCompiledEval(keyDef
->usecomp
, xpctxt
);
758 if (useRes
== NULL
) {
759 xsltTransformError(ctxt
, NULL
, keyDef
->inst
,
760 "Failed to evaluate the 'use' expression.\n");
761 ctxt
->state
= XSLT_STATE_STOPPED
;
764 if (useRes
->type
== XPATH_NODESET
) {
765 if ((useRes
->nodesetval
!= NULL
) &&
766 (useRes
->nodesetval
->nodeNr
!= 0))
768 len
= useRes
->nodesetval
->nodeNr
;
769 str
= xmlXPathCastNodeToString(useRes
->nodesetval
->nodeTab
[0]);
775 if (useRes
->type
== XPATH_STRING
) {
777 * Consume the string value.
779 str
= useRes
->stringval
;
780 useRes
->stringval
= NULL
;
782 str
= xmlXPathCastToString(useRes
);
786 * Process all strings.
793 #ifdef WITH_XSLT_DEBUG_KEYS
794 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
,
795 "xsl:key : node associated to ('%s', '%s')\n", keyDef
->name
, str
));
798 keylist
= xmlHashLookup(table
->keys
, str
);
799 if (keylist
== NULL
) {
800 keylist
= xmlXPathNodeSetCreate(cur
);
803 xmlHashAddEntry(table
->keys
, str
, keylist
);
806 * TODO: How do we know if this function failed?
808 xmlXPathNodeSetAdd(keylist
, cur
);
811 case XML_ELEMENT_NODE
:
813 case XML_CDATA_SECTION_NODE
:
815 case XML_COMMENT_NODE
:
818 case XML_ATTRIBUTE_NODE
:
819 ((xmlAttrPtr
) cur
)->psvi
= keyDef
;
821 case XML_DOCUMENT_NODE
:
822 case XML_HTML_DOCUMENT_NODE
:
823 ((xmlDocPtr
) cur
)->psvi
= keyDef
;
835 str
= xmlXPathCastNodeToString(useRes
->nodesetval
->nodeTab
[k
]);
841 ctxt
->keyInitLevel
--;
843 * Restore context state.
845 xpctxt
->doc
= oldXPDoc
;
846 xpctxt
->nsNr
= oldXPNsNr
;
847 xpctxt
->namespaces
= oldXPNamespaces
;
848 xpctxt
->proximityPosition
= oldXPPos
;
849 xpctxt
->contextSize
= oldXPSize
;
851 ctxt
->node
= oldContextNode
;
852 ctxt
->document
= oldDocInfo
;
853 ctxt
->inst
= oldInst
;
858 xmlXPathFreeObject(useRes
);
859 if (matchRes
!= NULL
)
860 xmlXPathFreeObject(matchRes
);
866 * @ctxt: an XSLT transformation context
867 * @idoc: a document info
869 * Computes all the keys tables for the current input document.
870 * Should be done before global varibales are initialized.
871 * NOTE: Not used anymore in the refactored code.
874 xsltInitCtxtKeys(xsltTransformContextPtr ctxt
, xsltDocumentPtr idoc
) {
875 xsltStylesheetPtr style
;
876 xsltKeyDefPtr keyDef
;
878 if ((ctxt
== NULL
) || (idoc
== NULL
))
881 #ifdef KEY_INIT_DEBUG
882 fprintf(stderr
, "xsltInitCtxtKeys on document\n");
885 #ifdef WITH_XSLT_DEBUG_KEYS
886 if ((idoc
->doc
!= NULL
) && (idoc
->doc
->URL
!= NULL
))
887 XSLT_TRACE(ctxt
,XSLT_TRACE_KEYS
,xsltGenericDebug(xsltGenericDebugContext
, "Initializing keys on %s\n",
891 while (style
!= NULL
) {
892 keyDef
= (xsltKeyDefPtr
) style
->keys
;
893 while (keyDef
!= NULL
) {
894 xsltInitCtxtKey(ctxt
, idoc
, keyDef
);
896 keyDef
= keyDef
->next
;
899 style
= xsltNextImport(style
);
902 #ifdef KEY_INIT_DEBUG
903 fprintf(stderr
, "xsltInitCtxtKeys on document: done\n");
909 * xsltFreeDocumentKeys:
910 * @idoc: a XSLT document
912 * Free the keys associated to a document
915 xsltFreeDocumentKeys(xsltDocumentPtr idoc
) {
917 xsltFreeKeyTableList(idoc
->keys
);