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