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