Coverage Report

Created: 2024-05-15 07:11

/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
    key->match = xmlStrdup(match);
298
0
    key->use = xmlStrdup(use);
299
0
    key->inst = inst;
300
0
    key->nsList = xmlGetNsList(inst->doc, inst);
301
0
    if (key->nsList != NULL) {
302
0
        while (key->nsList[i] != NULL)
303
0
      i++;
304
0
    }
305
0
    key->nsNr = i;
306
307
    /*
308
     * Split the | and register it as as many keys
309
     */
310
0
    current = end = 0;
311
0
    while (match[current] != 0) {
312
0
  start = current;
313
0
  while (xmlIsBlank_ch(match[current]))
314
0
      current++;
315
0
  end = current;
316
0
  while ((match[end] != 0) && (match[end] != '|')) {
317
0
      if (match[end] == '[') {
318
0
          end = skipPredicate(match, end);
319
0
    if (end <= 0) {
320
0
        xsltTransformError(NULL, style, inst,
321
0
            "xsl:key : 'match' pattern is malformed: %s",
322
0
            key->match);
323
0
        if (style != NULL) style->errors++;
324
0
        goto error;
325
0
    }
326
0
      } else
327
0
    end++;
328
0
  }
329
0
  if (current == end) {
330
0
      xsltTransformError(NULL, style, inst,
331
0
             "xsl:key : 'match' pattern is empty\n");
332
0
      if (style != NULL) style->errors++;
333
0
      goto error;
334
0
  }
335
0
  if (match[start] != '/') {
336
0
      pattern = xmlStrcat(pattern, (xmlChar *)"//");
337
0
      if (pattern == NULL) {
338
0
    if (style != NULL) style->errors++;
339
0
    goto error;
340
0
      }
341
0
  }
342
0
  pattern = xmlStrncat(pattern, &match[start], end - start);
343
0
  if (pattern == NULL) {
344
0
      if (style != NULL) style->errors++;
345
0
      goto error;
346
0
  }
347
348
0
  if (match[end] == '|') {
349
0
      pattern = xmlStrcat(pattern, (xmlChar *)"|");
350
0
      end++;
351
0
  }
352
0
  current = end;
353
0
    }
354
0
    if (pattern == NULL) {
355
0
        xsltTransformError(NULL, style, inst,
356
0
                           "xsl:key : 'match' pattern is empty\n");
357
0
        if (style != NULL) style->errors++;
358
0
        goto error;
359
0
    }
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
0
#ifdef XML_XPATH_NOVAR
373
0
    key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR);
374
#else
375
    key->comp = xsltXPathCompile(style, pattern);
376
#endif
377
0
    if (key->comp == NULL) {
378
0
  xsltTransformError(NULL, style, inst,
379
0
    "xsl:key : 'match' pattern compilation failed '%s'\n",
380
0
             pattern);
381
0
  if (style != NULL) style->errors++;
382
0
    }
383
0
#ifdef XML_XPATH_NOVAR
384
0
    key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR);
385
#else
386
    key->usecomp = xsltXPathCompile(style, use);
387
#endif
388
0
    if (key->usecomp == NULL) {
389
0
  xsltTransformError(NULL, style, inst,
390
0
    "xsl:key : 'use' expression compilation failed '%s'\n",
391
0
             use);
392
0
  if (style != NULL) style->errors++;
393
0
    }
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
0
    if (style->keys == NULL) {
401
0
  style->keys = key;
402
0
    } else {
403
0
        xsltKeyDefPtr prev = style->keys;
404
405
0
  while (prev->next != NULL)
406
0
      prev = prev->next;
407
408
0
  prev->next = key;
409
0
    }
410
0
    key->next = NULL;
411
0
    key = NULL;
412
413
0
error:
414
0
    if (pattern != NULL)
415
0
  xmlFree(pattern);
416
0
    if (key != NULL)
417
0
        xsltFreeKeyDef(key);
418
0
    return(0);
419
0
}
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
435
xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
436
300
     const xmlChar *nameURI, const xmlChar *value) {
437
300
    xmlNodeSetPtr ret;
438
300
    xsltKeyTablePtr table;
439
300
    int init_table = 0;
440
441
300
    if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
442
300
  (ctxt->document == NULL))
443
0
  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
300
    if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) &&
454
300
        (ctxt->keyInitLevel == 0)) {
455
        /*
456
   * If non-recursive behaviour, just try to initialize all keys
457
   */
458
0
  if (xsltInitAllDocKeys(ctxt))
459
0
      return(NULL);
460
0
    }
461
462
300
retry:
463
300
    table = (xsltKeyTablePtr) ctxt->document->keys;
464
300
    while (table != NULL) {
465
0
  if (((nameURI != NULL) == (table->nameURI != NULL)) &&
466
0
      xmlStrEqual(table->name, name) &&
467
0
      xmlStrEqual(table->nameURI, nameURI))
468
0
  {
469
0
      ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
470
0
      return(ret);
471
0
  }
472
0
  table = table->next;
473
0
    }
474
475
300
    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
0
        xsltInitDocKeyTable(ctxt, name, nameURI);
481
0
  init_table = 1;
482
0
  goto retry;
483
0
    }
484
485
300
    return(NULL);
486
300
}
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
497
xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
498
                    const xmlChar *nameURI)
499
0
{
500
0
    xsltStylesheetPtr style;
501
0
    xsltKeyDefPtr keyd = NULL;
502
0
    int found = 0;
503
504
#ifdef KEY_INIT_DEBUG
505
fprintf(stderr, "xsltInitDocKeyTable %s\n", name);
506
#endif
507
508
0
    style = ctxt->style;
509
0
    while (style != NULL) {
510
0
  keyd = (xsltKeyDefPtr) style->keys;
511
0
  while (keyd != NULL) {
512
0
      if (((keyd->nameURI != NULL) ==
513
0
     (nameURI != NULL)) &&
514
0
    xmlStrEqual(keyd->name, name) &&
515
0
    xmlStrEqual(keyd->nameURI, nameURI))
516
0
      {
517
0
    xsltInitCtxtKey(ctxt, ctxt->document, keyd);
518
0
    if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
519
0
        return(0);
520
0
    found = 1;
521
0
      }
522
0
      keyd = keyd->next;
523
0
  }
524
0
  style = xsltNextImport(style);
525
0
    }
526
0
    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
0
  xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL,
532
0
      "Failed to find key definition for %s\n", name);
533
0
  ctxt->state = XSLT_STATE_STOPPED;
534
0
        return(-1);
535
0
    }
536
#ifdef KEY_INIT_DEBUG
537
fprintf(stderr, "xsltInitDocKeyTable %s done\n", name);
538
#endif
539
0
    return(0);
540
0
}
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
553
xsltInitAllDocKeys(xsltTransformContextPtr ctxt)
554
0
{
555
0
    xsltStylesheetPtr style;
556
0
    xsltKeyDefPtr keyd;
557
0
    xsltKeyTablePtr table;
558
559
0
    if (ctxt == NULL)
560
0
  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
0
    if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
568
0
  return(0);
569
570
571
    /*
572
    * TODO: This could be further optimized
573
    */
574
0
    style = ctxt->style;
575
0
    while (style) {
576
0
  keyd = (xsltKeyDefPtr) style->keys;
577
0
  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
0
      table = (xsltKeyTablePtr) ctxt->document->keys;
586
0
      while (table) {
587
0
    if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
588
0
        xmlStrEqual(keyd->name, table->name) &&
589
0
        xmlStrEqual(keyd->nameURI, table->nameURI))
590
0
    {
591
0
        break;
592
0
    }
593
0
    table = table->next;
594
0
      }
595
0
      if (table == NULL) {
596
    /*
597
    * Keys with this QName have not been yet computed.
598
    */
599
0
    xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI);
600
0
      }
601
0
      keyd = keyd->next;
602
0
  }
603
0
  style = xsltNextImport(style);
604
0
    }
605
#ifdef KEY_INIT_DEBUG
606
fprintf(stderr, "xsltInitAllDocKeys: done\n");
607
#endif
608
0
    return(0);
609
0
}
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
622
xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc,
623
          xsltKeyDefPtr keyDef)
624
0
{
625
0
    int i, len, k;
626
0
    xmlNodeSetPtr matchList = NULL, keylist;
627
0
    xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
628
0
    xmlChar *str = NULL;
629
0
    xsltKeyTablePtr table;
630
0
    xmlNodePtr oldInst, cur;
631
0
    xmlNodePtr oldContextNode;
632
0
    xsltDocumentPtr oldDocInfo;
633
0
    int oldXPPos, oldXPSize;
634
0
    xmlNodePtr oldXPNode;
635
0
    xmlDocPtr oldXPDoc;
636
0
    int oldXPNsNr;
637
0
    xmlNsPtr *oldXPNamespaces;
638
0
    xmlXPathContextPtr xpctxt;
639
640
#ifdef KEY_INIT_DEBUG
641
fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel);
642
#endif
643
644
0
    if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
645
0
  return(-1);
646
647
    /*
648
     * Detect recursive keys
649
     */
650
0
    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
0
  xsltTransformError(ctxt, NULL, keyDef->inst,
658
0
      "Key definition for %s is recursive\n", keyDef->name);
659
0
  ctxt->state = XSLT_STATE_STOPPED;
660
0
        return(-1);
661
0
    }
662
0
    ctxt->keyInitLevel++;
663
664
0
    xpctxt = ctxt->xpathCtxt;
665
0
    idoc->nbKeysComputed++;
666
    /*
667
    * Save context state.
668
    */
669
0
    oldInst = ctxt->inst;
670
0
    oldDocInfo = ctxt->document;
671
0
    oldContextNode = ctxt->node;
672
673
0
    oldXPNode = xpctxt->node;
674
0
    oldXPDoc = xpctxt->doc;
675
0
    oldXPPos = xpctxt->proximityPosition;
676
0
    oldXPSize = xpctxt->contextSize;
677
0
    oldXPNsNr = xpctxt->nsNr;
678
0
    oldXPNamespaces = xpctxt->namespaces;
679
680
    /*
681
    * Set up contexts.
682
    */
683
0
    ctxt->document = idoc;
684
0
    ctxt->node = (xmlNodePtr) idoc->doc;
685
0
    ctxt->inst = keyDef->inst;
686
687
0
    xpctxt->doc = idoc->doc;
688
0
    xpctxt->node = (xmlNodePtr) idoc->doc;
689
    /* TODO : clarify the use of namespaces in keys evaluation */
690
0
    xpctxt->namespaces = keyDef->nsList;
691
0
    xpctxt->nsNr = keyDef->nsNr;
692
693
    /*
694
    * Evaluate the 'match' expression of the xsl:key.
695
    * TODO: The 'match' is a *pattern*.
696
    */
697
0
    matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
698
0
    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
0
  xsltTransformError(ctxt, NULL, keyDef->inst,
705
0
      "Failed to evaluate the 'match' expression.\n");
706
0
  ctxt->state = XSLT_STATE_STOPPED;
707
0
  goto error;
708
0
    } else {
709
0
  if (matchRes->type == XPATH_NODESET) {
710
0
      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
0
  } 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
0
      xsltTransformError(ctxt, NULL, keyDef->inst,
727
0
    "The 'match' expression did not evaluate to a node set.\n");
728
0
      ctxt->state = XSLT_STATE_STOPPED;
729
0
      goto error;
730
0
  }
731
0
    }
732
0
    if ((matchList == NULL) || (matchList->nodeNr <= 0))
733
0
  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
0
    table = (xsltKeyTablePtr) idoc->keys;
740
0
    while (table != NULL) {
741
0
        if (xmlStrEqual(table->name, keyDef->name) &&
742
0
      (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
743
0
       ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
744
0
        (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
745
0
      break;
746
0
  table = table->next;
747
0
    }
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
0
    if (table == NULL) {
753
0
        table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
754
0
        if (table == NULL)
755
0
      goto error;
756
0
        table->next = idoc->keys;
757
0
        idoc->keys = table;
758
0
    }
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
0
    xpctxt->contextSize = 1;
767
0
    xpctxt->proximityPosition = 1;
768
769
0
    for (i = 0; i < matchList->nodeNr; i++) {
770
0
  cur = matchList->nodeTab[i];
771
0
  if (! IS_XSLT_REAL_NODE(cur))
772
0
      continue;
773
0
        ctxt->node = cur;
774
0
  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
0
  if (useRes != NULL)
783
0
      xmlXPathFreeObject(useRes);
784
0
  useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
785
0
  if (useRes == NULL) {
786
0
      xsltTransformError(ctxt, NULL, keyDef->inst,
787
0
    "Failed to evaluate the 'use' expression.\n");
788
0
      ctxt->state = XSLT_STATE_STOPPED;
789
0
      break;
790
0
  }
791
0
  if (useRes->type == XPATH_NODESET) {
792
0
      if ((useRes->nodesetval != NULL) &&
793
0
    (useRes->nodesetval->nodeNr != 0))
794
0
      {
795
0
    len = useRes->nodesetval->nodeNr;
796
0
    str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
797
0
      } else {
798
0
    continue;
799
0
      }
800
0
  } else {
801
0
      len = 1;
802
0
      if (useRes->type == XPATH_STRING) {
803
    /*
804
    * Consume the string value.
805
    */
806
0
    str = useRes->stringval;
807
0
    useRes->stringval = NULL;
808
0
      } else {
809
0
    str = xmlXPathCastToString(useRes);
810
0
      }
811
0
  }
812
  /*
813
  * Process all strings.
814
  */
815
0
  k = 0;
816
0
  while (1) {
817
0
      if (str == NULL)
818
0
    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
0
      keylist = xmlHashLookup(table->keys, str);
826
0
      if (keylist == NULL) {
827
0
    keylist = xmlXPathNodeSetCreate(cur);
828
0
    if (keylist == NULL)
829
0
        goto error;
830
0
    xmlHashAddEntry(table->keys, str, keylist);
831
0
      } else {
832
    /*
833
    * TODO: How do we know if this function failed?
834
    */
835
0
    xmlXPathNodeSetAdd(keylist, cur);
836
0
      }
837
0
            xsltSetSourceNodeFlags(ctxt, cur, XSLT_SOURCE_NODE_HAS_KEY);
838
0
      xmlFree(str);
839
0
      str = NULL;
840
841
0
next_string:
842
0
      k++;
843
0
      if (k >= len)
844
0
    break;
845
0
      str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
846
0
  }
847
0
    }
848
849
0
exit:
850
0
error:
851
0
    ctxt->keyInitLevel--;
852
    /*
853
    * Restore context state.
854
    */
855
0
    xpctxt->node = oldXPNode;
856
0
    xpctxt->doc = oldXPDoc;
857
0
    xpctxt->nsNr = oldXPNsNr;
858
0
    xpctxt->namespaces = oldXPNamespaces;
859
0
    xpctxt->proximityPosition = oldXPPos;
860
0
    xpctxt->contextSize = oldXPSize;
861
862
0
    ctxt->node = oldContextNode;
863
0
    ctxt->document = oldDocInfo;
864
0
    ctxt->inst = oldInst;
865
866
0
    if (str)
867
0
  xmlFree(str);
868
0
    if (useRes != NULL)
869
0
  xmlXPathFreeObject(useRes);
870
0
    if (matchRes != NULL)
871
0
  xmlXPathFreeObject(matchRes);
872
0
    return(0);
873
0
}
874
875
/**
876
 * xsltInitCtxtKeys:
877
 * @ctxt:  an XSLT transformation context
878
 * @idoc:  a document info
879
 *
880
 * Computes all the keys tables for the current input document.
881
 * Should be done before global varibales are initialized.
882
 * NOTE: Not used anymore in the refactored code.
883
 */
884
void
885
0
xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) {
886
0
    xsltStylesheetPtr style;
887
0
    xsltKeyDefPtr keyDef;
888
889
0
    if ((ctxt == NULL) || (idoc == NULL))
890
0
  return;
891
892
#ifdef KEY_INIT_DEBUG
893
fprintf(stderr, "xsltInitCtxtKeys on document\n");
894
#endif
895
896
#ifdef WITH_XSLT_DEBUG_KEYS
897
    if ((idoc->doc != NULL) && (idoc->doc->URL != NULL))
898
  XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
899
         idoc->doc->URL));
900
#endif
901
0
    style = ctxt->style;
902
0
    while (style != NULL) {
903
0
  keyDef = (xsltKeyDefPtr) style->keys;
904
0
  while (keyDef != NULL) {
905
0
      xsltInitCtxtKey(ctxt, idoc, keyDef);
906
907
0
      keyDef = keyDef->next;
908
0
  }
909
910
0
  style = xsltNextImport(style);
911
0
    }
912
913
#ifdef KEY_INIT_DEBUG
914
fprintf(stderr, "xsltInitCtxtKeys on document: done\n");
915
#endif
916
917
0
}
918
919
/**
920
 * xsltFreeDocumentKeys:
921
 * @idoc: a XSLT document
922
 *
923
 * Free the keys associated to a document
924
 */
925
void
926
0
xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
927
0
    if (idoc != NULL)
928
0
        xsltFreeKeyTableList(idoc->keys);
929
0
}
930