1/*
2 * keys.c: Implemetation of the keys support
3 *
4 * Reference:
5 *   http://www.w3.org/TR/1999/REC-xslt-19991116
6 *
7 * See Copyright for the status of this software.
8 *
9 * daniel@veillard.com
10 */
11
12#define IN_LIBXSLT
13#include "libxslt.h"
14
15#include <string.h>
16
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>
24#include <libxml/xpath.h>
25#include "xslt.h"
26#include "xsltInternals.h"
27#include "xsltutils.h"
28#include "imports.h"
29#include "templates.h"
30#include "keys.h"
31
32#ifdef WITH_XSLT_DEBUG
33#define WITH_XSLT_DEBUG_KEYS
34#endif
35
36static int
37xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
38                    const xmlChar *nameURI);
39
40/************************************************************************
41 *									*
42 *			Type functions					*
43 *									*
44 ************************************************************************/
45
46/**
47 * xsltNewKeyDef:
48 * @name:  the key name or NULL
49 * @nameURI:  the name URI or NULL
50 *
51 * Create a new XSLT KeyDef
52 *
53 * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
54 */
55static xsltKeyDefPtr
56xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
57    xsltKeyDefPtr cur;
58
59    cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
60    if (cur == NULL) {
61	xsltTransformError(NULL, NULL, NULL,
62		"xsltNewKeyDef : malloc failed\n");
63	return(NULL);
64    }
65    memset(cur, 0, sizeof(xsltKeyDef));
66    if (name != NULL)
67	cur->name = xmlStrdup(name);
68    if (nameURI != NULL)
69	cur->nameURI = xmlStrdup(nameURI);
70    cur->nsList = NULL;
71    return(cur);
72}
73
74/**
75 * xsltFreeKeyDef:
76 * @keyd:  an XSLT key definition
77 *
78 * Free up the memory allocated by @keyd
79 */
80static void
81xsltFreeKeyDef(xsltKeyDefPtr keyd) {
82    if (keyd == NULL)
83	return;
84    if (keyd->comp != NULL)
85	xmlXPathFreeCompExpr(keyd->comp);
86    if (keyd->usecomp != NULL)
87	xmlXPathFreeCompExpr(keyd->usecomp);
88    if (keyd->name != NULL)
89	xmlFree(keyd->name);
90    if (keyd->nameURI != NULL)
91	xmlFree(keyd->nameURI);
92    if (keyd->match != NULL)
93	xmlFree(keyd->match);
94    if (keyd->use != NULL)
95	xmlFree(keyd->use);
96    if (keyd->nsList != NULL)
97        xmlFree(keyd->nsList);
98    memset(keyd, -1, sizeof(xsltKeyDef));
99    xmlFree(keyd);
100}
101
102/**
103 * xsltFreeKeyDefList:
104 * @keyd:  an XSLT key definition list
105 *
106 * Free up the memory allocated by all the elements of @keyd
107 */
108static void
109xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
110    xsltKeyDefPtr cur;
111
112    while (keyd != NULL) {
113	cur = keyd;
114	keyd = keyd->next;
115	xsltFreeKeyDef(cur);
116    }
117}
118
119/**
120 * xsltNewKeyTable:
121 * @name:  the key name or NULL
122 * @nameURI:  the name URI or NULL
123 *
124 * Create a new XSLT KeyTable
125 *
126 * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
127 */
128static xsltKeyTablePtr
129xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
130    xsltKeyTablePtr cur;
131
132    cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
133    if (cur == NULL) {
134	xsltTransformError(NULL, NULL, NULL,
135		"xsltNewKeyTable : malloc failed\n");
136	return(NULL);
137    }
138    memset(cur, 0, sizeof(xsltKeyTable));
139    if (name != NULL)
140	cur->name = xmlStrdup(name);
141    if (nameURI != NULL)
142	cur->nameURI = xmlStrdup(nameURI);
143    cur->keys = xmlHashCreate(0);
144    return(cur);
145}
146
147/**
148 * xsltFreeKeyTable:
149 * @keyt:  an XSLT key table
150 *
151 * Free up the memory allocated by @keyt
152 */
153static void
154xsltFreeKeyTable(xsltKeyTablePtr keyt) {
155    if (keyt == NULL)
156	return;
157    if (keyt->name != NULL)
158	xmlFree(keyt->name);
159    if (keyt->nameURI != NULL)
160	xmlFree(keyt->nameURI);
161    if (keyt->keys != NULL)
162	xmlHashFree(keyt->keys,
163		    (xmlHashDeallocator) xmlXPathFreeNodeSet);
164    memset(keyt, -1, sizeof(xsltKeyTable));
165    xmlFree(keyt);
166}
167
168/**
169 * xsltFreeKeyTableList:
170 * @keyt:  an XSLT key table list
171 *
172 * Free up the memory allocated by all the elements of @keyt
173 */
174static void
175xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
176    xsltKeyTablePtr cur;
177
178    while (keyt != NULL) {
179	cur = keyt;
180	keyt = keyt->next;
181	xsltFreeKeyTable(cur);
182    }
183}
184
185/************************************************************************
186 *									*
187 *		The interpreter for the precompiled patterns		*
188 *									*
189 ************************************************************************/
190
191
192/**
193 * xsltFreeKeys:
194 * @style: an XSLT stylesheet
195 *
196 * Free up the memory used by XSLT keys in a stylesheet
197 */
198void
199xsltFreeKeys(xsltStylesheetPtr style) {
200    if (style->keys)
201	xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
202}
203
204/**
205 * skipString:
206 * @cur: the current pointer
207 * @end: the current offset
208 *
209 * skip a string delimited by " or '
210 *
211 * Returns the byte after the string or -1 in case of error
212 */
213static int
214skipString(const xmlChar *cur, int end) {
215    xmlChar limit;
216
217    if ((cur == NULL) || (end < 0)) return(-1);
218    if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end];
219    else return(end);
220    end++;
221    while (cur[end] != 0) {
222        if (cur[end] == limit)
223	    return(end + 1);
224	end++;
225    }
226    return(-1);
227}
228
229/**
230 * skipPredicate:
231 * @cur: the current pointer
232 * @end: the current offset
233 *
234 * skip a predicate
235 *
236 * Returns the byte after the predicate or -1 in case of error
237 */
238static int
239skipPredicate(const xmlChar *cur, int end) {
240    if ((cur == NULL) || (end < 0)) return(-1);
241    if (cur[end] != '[') return(end);
242    end++;
243    while (cur[end] != 0) {
244        if ((cur[end] == '\'') || (cur[end] == '"')) {
245	    end = skipString(cur, end);
246	    if (end <= 0)
247	        return(-1);
248	    continue;
249	} else if (cur[end] == '[') {
250	    end = skipPredicate(cur, end);
251	    if (end <= 0)
252	        return(-1);
253	    continue;
254	} else if (cur[end] == ']')
255	    return(end + 1);
256	end++;
257    }
258    return(-1);
259}
260
261/**
262 * xsltAddKey:
263 * @style: an XSLT stylesheet
264 * @name:  the key name or NULL
265 * @nameURI:  the name URI or NULL
266 * @match:  the match value
267 * @use:  the use value
268 * @inst: the key instruction
269 *
270 * add a key definition to a stylesheet
271 *
272 * Returns 0 in case of success, and -1 in case of failure.
273 */
274int
275xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
276	   const xmlChar *nameURI, const xmlChar *match,
277	   const xmlChar *use, xmlNodePtr inst) {
278    xsltKeyDefPtr key;
279    xmlChar *pattern = NULL;
280    int current, end, start, i = 0;
281
282    if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
283	return(-1);
284
285#ifdef WITH_XSLT_DEBUG_KEYS
286    xsltGenericDebug(xsltGenericDebugContext,
287	"Add key %s, match %s, use %s\n", name, match, use);
288#endif
289
290    key = xsltNewKeyDef(name, nameURI);
291    key->match = xmlStrdup(match);
292    key->use = xmlStrdup(use);
293    key->inst = inst;
294    key->nsList = xmlGetNsList(inst->doc, inst);
295    if (key->nsList != NULL) {
296        while (key->nsList[i] != NULL)
297	    i++;
298    }
299    key->nsNr = i;
300
301    /*
302     * Split the | and register it as as many keys
303     */
304    current = end = 0;
305    while (match[current] != 0) {
306	start = current;
307	while (IS_BLANK_CH(match[current]))
308	    current++;
309	end = current;
310	while ((match[end] != 0) && (match[end] != '|')) {
311	    if (match[end] == '[') {
312	        end = skipPredicate(match, end);
313		if (end <= 0) {
314		    xsltTransformError(NULL, style, inst,
315		        "xsl:key : 'match' pattern is malformed: %s",
316		        key->match);
317		    if (style != NULL) style->errors++;
318		    goto error;
319		}
320	    } else
321		end++;
322	}
323	if (current == end) {
324	    xsltTransformError(NULL, style, inst,
325			       "xsl:key : 'match' pattern is empty\n");
326	    if (style != NULL) style->errors++;
327	    goto error;
328	}
329	if (match[start] != '/') {
330	    pattern = xmlStrcat(pattern, (xmlChar *)"//");
331	    if (pattern == NULL) {
332		if (style != NULL) style->errors++;
333		goto error;
334	    }
335	}
336	pattern = xmlStrncat(pattern, &match[start], end - start);
337	if (pattern == NULL) {
338	    if (style != NULL) style->errors++;
339	    goto error;
340	}
341
342	if (match[end] == '|') {
343	    pattern = xmlStrcat(pattern, (xmlChar *)"|");
344	    end++;
345	}
346	current = end;
347    }
348    if (pattern == NULL) {
349        xsltTransformError(NULL, style, inst,
350                           "xsl:key : 'match' pattern is empty\n");
351        if (style != NULL) style->errors++;
352        goto error;
353    }
354#ifdef WITH_XSLT_DEBUG_KEYS
355    xsltGenericDebug(xsltGenericDebugContext,
356	"   resulting pattern %s\n", pattern);
357#endif
358    /*
359    * XSLT-1: "It is an error for the value of either the use
360    *  attribute or the match attribute to contain a
361    *  VariableReference."
362    * TODO: We should report a variable-reference at compile-time.
363    *   Maybe a search for "$", if it occurs outside of quotation
364    *   marks, could be sufficient.
365    */
366#ifdef XML_XPATH_NOVAR
367    key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR);
368#else
369    key->comp = xsltXPathCompile(style, pattern);
370#endif
371    if (key->comp == NULL) {
372	xsltTransformError(NULL, style, inst,
373		"xsl:key : 'match' pattern compilation failed '%s'\n",
374		         pattern);
375	if (style != NULL) style->errors++;
376    }
377#ifdef XML_XPATH_NOVAR
378    key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR);
379#else
380    key->usecomp = xsltXPathCompile(style, use);
381#endif
382    if (key->usecomp == NULL) {
383	xsltTransformError(NULL, style, inst,
384		"xsl:key : 'use' expression compilation failed '%s'\n",
385		         use);
386	if (style != NULL) style->errors++;
387    }
388
389    /*
390     * Sometimes the stylesheet writer use the order to ease the
391     * resolution of keys when they are dependant, keep the provided
392     * order so add the new one at the end.
393     */
394    if (style->keys == NULL) {
395	style->keys = key;
396    } else {
397        xsltKeyDefPtr prev = style->keys;
398
399	while (prev->next != NULL)
400	    prev = prev->next;
401
402	prev->next = key;
403    }
404    key->next = NULL;
405
406error:
407    if (pattern != NULL)
408	xmlFree(pattern);
409    return(0);
410}
411
412/**
413 * xsltGetKey:
414 * @ctxt: an XSLT transformation context
415 * @name:  the key name or NULL
416 * @nameURI:  the name URI or NULL
417 * @value:  the key value to look for
418 *
419 * Looks up a key of the in current source doc (the document info
420 * on @ctxt->document). Computes the key if not already done
421 * for the current source doc.
422 *
423 * Returns the nodeset resulting from the query or NULL
424 */
425xmlNodeSetPtr
426xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
427	   const xmlChar *nameURI, const xmlChar *value) {
428    xmlNodeSetPtr ret;
429    xsltKeyTablePtr table;
430    int init_table = 0;
431
432    if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
433	(ctxt->document == NULL))
434	return(NULL);
435
436#ifdef WITH_XSLT_DEBUG_KEYS
437    xsltGenericDebug(xsltGenericDebugContext,
438	"Get key %s, value %s\n", name, value);
439#endif
440
441    /*
442     * keys are computed only on-demand on first key access for a document
443     */
444    if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) &&
445        (ctxt->keyInitLevel == 0)) {
446        /*
447	 * If non-recursive behaviour, just try to initialize all keys
448	 */
449	if (xsltInitAllDocKeys(ctxt))
450	    return(NULL);
451    }
452
453retry:
454    table = (xsltKeyTablePtr) ctxt->document->keys;
455    while (table != NULL) {
456	if (((nameURI != NULL) == (table->nameURI != NULL)) &&
457	    xmlStrEqual(table->name, name) &&
458	    xmlStrEqual(table->nameURI, nameURI))
459	{
460	    ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
461	    return(ret);
462	}
463	table = table->next;
464    }
465
466    if ((ctxt->keyInitLevel != 0) && (init_table == 0)) {
467        /*
468	 * Apparently one key is recursive and this one is needed,
469	 * initialize just it, that time and retry
470	 */
471        xsltInitDocKeyTable(ctxt, name, nameURI);
472	init_table = 1;
473	goto retry;
474    }
475
476    return(NULL);
477}
478
479
480/**
481 * xsltInitDocKeyTable:
482 *
483 * INTERNAL ROUTINE ONLY
484 *
485 * Check if any keys on the current document need to be computed
486 */
487static int
488xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
489                    const xmlChar *nameURI)
490{
491    xsltStylesheetPtr style;
492    xsltKeyDefPtr keyd = NULL;
493    int found = 0;
494
495#ifdef KEY_INIT_DEBUG
496fprintf(stderr, "xsltInitDocKeyTable %s\n", name);
497#endif
498
499    style = ctxt->style;
500    while (style != NULL) {
501	keyd = (xsltKeyDefPtr) style->keys;
502	while (keyd != NULL) {
503	    if (((keyd->nameURI != NULL) ==
504		 (nameURI != NULL)) &&
505		xmlStrEqual(keyd->name, name) &&
506		xmlStrEqual(keyd->nameURI, nameURI))
507	    {
508		xsltInitCtxtKey(ctxt, ctxt->document, keyd);
509		if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
510		    return(0);
511		found = 1;
512	    }
513	    keyd = keyd->next;
514	}
515	style = xsltNextImport(style);
516    }
517    if (found == 0) {
518#ifdef WITH_XSLT_DEBUG_KEYS
519	XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
520	     "xsltInitDocKeyTable: did not found %s\n", name));
521#endif
522	xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL,
523	    "Failed to find key definition for %s\n", name);
524	ctxt->state = XSLT_STATE_STOPPED;
525        return(-1);
526    }
527#ifdef KEY_INIT_DEBUG
528fprintf(stderr, "xsltInitDocKeyTable %s done\n", name);
529#endif
530    return(0);
531}
532
533/**
534 * xsltInitAllDocKeys:
535 * @ctxt: transformation context
536 *
537 * INTERNAL ROUTINE ONLY
538 *
539 * Check if any keys on the current document need to be computed
540 *
541 * Returns 0 in case of success, -1 in case of failure
542 */
543int
544xsltInitAllDocKeys(xsltTransformContextPtr ctxt)
545{
546    xsltStylesheetPtr style;
547    xsltKeyDefPtr keyd;
548    xsltKeyTablePtr table;
549
550    if (ctxt == NULL)
551	return(-1);
552
553#ifdef KEY_INIT_DEBUG
554fprintf(stderr, "xsltInitAllDocKeys %d %d\n",
555        ctxt->document->nbKeysComputed, ctxt->nbKeys);
556#endif
557
558    if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
559	return(0);
560
561
562    /*
563    * TODO: This could be further optimized
564    */
565    style = ctxt->style;
566    while (style) {
567	keyd = (xsltKeyDefPtr) style->keys;
568	while (keyd != NULL) {
569#ifdef KEY_INIT_DEBUG
570fprintf(stderr, "Init key %s\n", keyd->name);
571#endif
572	    /*
573	    * Check if keys with this QName have been already
574	    * computed.
575	    */
576	    table = (xsltKeyTablePtr) ctxt->document->keys;
577	    while (table) {
578		if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
579		    xmlStrEqual(keyd->name, table->name) &&
580		    xmlStrEqual(keyd->nameURI, table->nameURI))
581		{
582		    break;
583		}
584		table = table->next;
585	    }
586	    if (table == NULL) {
587		/*
588		* Keys with this QName have not been yet computed.
589		*/
590		xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI);
591	    }
592	    keyd = keyd->next;
593	}
594	style = xsltNextImport(style);
595    }
596#ifdef KEY_INIT_DEBUG
597fprintf(stderr, "xsltInitAllDocKeys: done\n");
598#endif
599    return(0);
600}
601
602/**
603 * xsltInitCtxtKey:
604 * @ctxt: an XSLT transformation context
605 * @idoc:  the document information (holds key values)
606 * @keyDef: the key definition
607 *
608 * Computes the key tables this key and for the current input document.
609 *
610 * Returns: 0 on success, -1 on error
611 */
612int
613xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc,
614	        xsltKeyDefPtr keyDef)
615{
616    int i, len, k;
617    xmlNodeSetPtr matchList = NULL, keylist;
618    xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
619    xmlChar *str = NULL;
620    xsltKeyTablePtr table;
621    xmlNodePtr oldInst, cur;
622    xmlNodePtr oldContextNode;
623    xsltDocumentPtr oldDocInfo;
624    int	oldXPPos, oldXPSize;
625    xmlDocPtr oldXPDoc;
626    int oldXPNsNr;
627    xmlNsPtr *oldXPNamespaces;
628    xmlXPathContextPtr xpctxt;
629
630#ifdef KEY_INIT_DEBUG
631fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel);
632#endif
633
634    if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
635	return(-1);
636
637    /*
638     * Detect recursive keys
639     */
640    if (ctxt->keyInitLevel > ctxt->nbKeys) {
641#ifdef WITH_XSLT_DEBUG_KEYS
642	XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,
643	           xsltGenericDebug(xsltGenericDebugContext,
644		       "xsltInitCtxtKey: key definition of %s is recursive\n",
645		       keyDef->name));
646#endif
647	xsltTransformError(ctxt, NULL, keyDef->inst,
648	    "Key definition for %s is recursive\n", keyDef->name);
649	ctxt->state = XSLT_STATE_STOPPED;
650        return(-1);
651    }
652    ctxt->keyInitLevel++;
653
654    xpctxt = ctxt->xpathCtxt;
655    idoc->nbKeysComputed++;
656    /*
657    * Save context state.
658    */
659    oldInst = ctxt->inst;
660    oldDocInfo = ctxt->document;
661    oldContextNode = ctxt->node;
662
663    oldXPDoc = xpctxt->doc;
664    oldXPPos = xpctxt->proximityPosition;
665    oldXPSize = xpctxt->contextSize;
666    oldXPNsNr = xpctxt->nsNr;
667    oldXPNamespaces = xpctxt->namespaces;
668
669    /*
670    * Set up contexts.
671    */
672    ctxt->document = idoc;
673    ctxt->node = (xmlNodePtr) idoc->doc;
674    ctxt->inst = keyDef->inst;
675
676    xpctxt->doc = idoc->doc;
677    xpctxt->node = (xmlNodePtr) idoc->doc;
678    /* TODO : clarify the use of namespaces in keys evaluation */
679    xpctxt->namespaces = keyDef->nsList;
680    xpctxt->nsNr = keyDef->nsNr;
681
682    /*
683    * Evaluate the 'match' expression of the xsl:key.
684    * TODO: The 'match' is a *pattern*.
685    */
686    matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
687    if (matchRes == NULL) {
688
689#ifdef WITH_XSLT_DEBUG_KEYS
690	XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
691	     "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match));
692#endif
693	xsltTransformError(ctxt, NULL, keyDef->inst,
694	    "Failed to evaluate the 'match' expression.\n");
695	ctxt->state = XSLT_STATE_STOPPED;
696	goto error;
697    } else {
698	if (matchRes->type == XPATH_NODESET) {
699	    matchList = matchRes->nodesetval;
700
701#ifdef WITH_XSLT_DEBUG_KEYS
702	    if (matchList != NULL)
703		XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
704		     "xsltInitCtxtKey: %s evaluates to %d nodes\n",
705				 keyDef->match, matchList->nodeNr));
706#endif
707	} else {
708	    /*
709	    * Is not a node set, but must be.
710	    */
711#ifdef WITH_XSLT_DEBUG_KEYS
712	    XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
713		 "xsltInitCtxtKey: %s is not a node set\n", keyDef->match));
714#endif
715	    xsltTransformError(ctxt, NULL, keyDef->inst,
716		"The 'match' expression did not evaluate to a node set.\n");
717	    ctxt->state = XSLT_STATE_STOPPED;
718	    goto error;
719	}
720    }
721    if ((matchList == NULL) || (matchList->nodeNr <= 0))
722	goto exit;
723
724    /**
725     * Multiple key definitions for the same name are allowed, so
726     * we must check if the key is already present for this doc
727     */
728    table = (xsltKeyTablePtr) idoc->keys;
729    while (table != NULL) {
730        if (xmlStrEqual(table->name, keyDef->name) &&
731	    (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
732	     ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
733	      (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
734	    break;
735	table = table->next;
736    }
737    /**
738     * If the key was not previously defined, create it now and
739     * chain it to the list of keys for the doc
740     */
741    if (table == NULL) {
742        table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
743        if (table == NULL)
744	    goto error;
745        table->next = idoc->keys;
746        idoc->keys = table;
747    }
748
749    /*
750    * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
751    * "...the use attribute of the xsl:key element is evaluated with x as
752    "  the current node and with a node list containing just x as the
753    *  current node list"
754    */
755    xpctxt->contextSize = 1;
756    xpctxt->proximityPosition = 1;
757
758    for (i = 0; i < matchList->nodeNr; i++) {
759	cur = matchList->nodeTab[i];
760	if (! IS_XSLT_REAL_NODE(cur))
761	    continue;
762	xpctxt->node = cur;
763	/*
764	* Process the 'use' of the xsl:key.
765	* SPEC XSLT 1.0:
766	* "The use attribute is an expression specifying the values of
767	*  the key; the expression is evaluated once for each node that
768	*  matches the pattern."
769	*/
770	if (useRes != NULL)
771	    xmlXPathFreeObject(useRes);
772	useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
773	if (useRes == NULL) {
774	    xsltTransformError(ctxt, NULL, keyDef->inst,
775		"Failed to evaluate the 'use' expression.\n");
776	    ctxt->state = XSLT_STATE_STOPPED;
777	    break;
778	}
779	if (useRes->type == XPATH_NODESET) {
780	    if ((useRes->nodesetval != NULL) &&
781		(useRes->nodesetval->nodeNr != 0))
782	    {
783		len = useRes->nodesetval->nodeNr;
784		str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
785	    } else {
786		continue;
787	    }
788	} else {
789	    len = 1;
790	    if (useRes->type == XPATH_STRING) {
791		/*
792		* Consume the string value.
793		*/
794		str = useRes->stringval;
795		useRes->stringval = NULL;
796	    } else {
797		str = xmlXPathCastToString(useRes);
798	    }
799	}
800	/*
801	* Process all strings.
802	*/
803	k = 0;
804	while (1) {
805	    if (str == NULL)
806		goto next_string;
807
808#ifdef WITH_XSLT_DEBUG_KEYS
809	    XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
810		"xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str));
811#endif
812
813	    keylist = xmlHashLookup(table->keys, str);
814	    if (keylist == NULL) {
815		keylist = xmlXPathNodeSetCreate(cur);
816		if (keylist == NULL)
817		    goto error;
818		xmlHashAddEntry(table->keys, str, keylist);
819	    } else {
820		/*
821		* TODO: How do we know if this function failed?
822		*/
823		xmlXPathNodeSetAdd(keylist, cur);
824	    }
825	    switch (cur->type) {
826		case XML_ELEMENT_NODE:
827		case XML_TEXT_NODE:
828		case XML_CDATA_SECTION_NODE:
829		case XML_PI_NODE:
830		case XML_COMMENT_NODE:
831		    cur->psvi = keyDef;
832		    break;
833		case XML_ATTRIBUTE_NODE:
834		    ((xmlAttrPtr) cur)->psvi = keyDef;
835		    break;
836		case XML_DOCUMENT_NODE:
837		case XML_HTML_DOCUMENT_NODE:
838		    ((xmlDocPtr) cur)->psvi = keyDef;
839		    break;
840		default:
841		    break;
842	    }
843	    xmlFree(str);
844	    str = NULL;
845
846next_string:
847	    k++;
848	    if (k >= len)
849		break;
850	    str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
851	}
852    }
853
854exit:
855error:
856    ctxt->keyInitLevel--;
857    /*
858    * Restore context state.
859    */
860    xpctxt->doc = oldXPDoc;
861    xpctxt->nsNr = oldXPNsNr;
862    xpctxt->namespaces = oldXPNamespaces;
863    xpctxt->proximityPosition = oldXPPos;
864    xpctxt->contextSize = oldXPSize;
865
866    ctxt->node = oldContextNode;
867    ctxt->document = oldDocInfo;
868    ctxt->inst = oldInst;
869
870    if (str)
871	xmlFree(str);
872    if (useRes != NULL)
873	xmlXPathFreeObject(useRes);
874    if (matchRes != NULL)
875	xmlXPathFreeObject(matchRes);
876    return(0);
877}
878
879/**
880 * xsltInitCtxtKeys:
881 * @ctxt:  an XSLT transformation context
882 * @idoc:  a document info
883 *
884 * Computes all the keys tables for the current input document.
885 * Should be done before global varibales are initialized.
886 * NOTE: Not used anymore in the refactored code.
887 */
888void
889xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) {
890    xsltStylesheetPtr style;
891    xsltKeyDefPtr keyDef;
892
893    if ((ctxt == NULL) || (idoc == NULL))
894	return;
895
896#ifdef KEY_INIT_DEBUG
897fprintf(stderr, "xsltInitCtxtKeys on document\n");
898#endif
899
900#ifdef WITH_XSLT_DEBUG_KEYS
901    if ((idoc->doc != NULL) && (idoc->doc->URL != NULL))
902	XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
903		     idoc->doc->URL));
904#endif
905    style = ctxt->style;
906    while (style != NULL) {
907	keyDef = (xsltKeyDefPtr) style->keys;
908	while (keyDef != NULL) {
909	    xsltInitCtxtKey(ctxt, idoc, keyDef);
910
911	    keyDef = keyDef->next;
912	}
913
914	style = xsltNextImport(style);
915    }
916
917#ifdef KEY_INIT_DEBUG
918fprintf(stderr, "xsltInitCtxtKeys on document: done\n");
919#endif
920
921}
922
923/**
924 * xsltFreeDocumentKeys:
925 * @idoc: a XSLT document
926 *
927 * Free the keys associated to a document
928 */
929void
930xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
931    if (idoc != NULL)
932        xsltFreeKeyTableList(idoc->keys);
933}
934
935