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