Coverage Report

Created: 2025-08-28 06:07

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