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