Coverage Report

Created: 2026-09-14 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxslt/libxslt/keys.c
Line
Count
Source
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
6.65k
xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
57
6.65k
    xsltKeyDefPtr cur;
58
59
6.65k
    cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
60
6.65k
    if (cur == NULL) {
61
1
  xsltTransformError(NULL, NULL, NULL,
62
1
    "xsltNewKeyDef : malloc failed\n");
63
1
  return(NULL);
64
1
    }
65
6.65k
    memset(cur, 0, sizeof(xsltKeyDef));
66
6.65k
    if (name != NULL)
67
6.65k
  cur->name = xmlStrdup(name);
68
6.65k
    if (nameURI != NULL)
69
2.86k
  cur->nameURI = xmlStrdup(nameURI);
70
6.65k
    cur->nsList = NULL;
71
6.65k
    return(cur);
72
6.65k
}
73
74
/**
75
 * xsltFreeKeyDef:
76
 * @keyd:  an XSLT key definition
77
 *
78
 * Free up the memory allocated by @keyd
79
 */
80
static void
81
6.65k
xsltFreeKeyDef(xsltKeyDefPtr keyd) {
82
6.65k
    if (keyd == NULL)
83
0
  return;
84
6.65k
    if (keyd->comp != NULL)
85
4.98k
  xmlXPathFreeCompExpr(keyd->comp);
86
6.65k
    if (keyd->usecomp != NULL)
87
5.49k
  xmlXPathFreeCompExpr(keyd->usecomp);
88
6.65k
    if (keyd->name != NULL)
89
6.65k
  xmlFree(keyd->name);
90
6.65k
    if (keyd->nameURI != NULL)
91
2.86k
  xmlFree(keyd->nameURI);
92
6.65k
    if (keyd->match != NULL)
93
6.65k
  xmlFree(keyd->match);
94
6.65k
    if (keyd->use != NULL)
95
6.65k
  xmlFree(keyd->use);
96
6.65k
    if (keyd->nsList != NULL)
97
6.64k
        xmlFree(keyd->nsList);
98
6.65k
    memset(keyd, -1, sizeof(xsltKeyDef));
99
6.65k
    xmlFree(keyd);
100
6.65k
}
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
1.50k
xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
110
1.50k
    xsltKeyDefPtr cur;
111
112
7.29k
    while (keyd != NULL) {
113
5.79k
  cur = keyd;
114
5.79k
  keyd = keyd->next;
115
5.79k
  xsltFreeKeyDef(cur);
116
5.79k
    }
117
1.50k
}
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
3.39k
xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
130
3.39k
    xsltKeyTablePtr cur;
131
132
3.39k
    cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
133
3.39k
    if (cur == NULL) {
134
2
  xsltTransformError(NULL, NULL, NULL,
135
2
    "xsltNewKeyTable : malloc failed\n");
136
2
  return(NULL);
137
2
    }
138
3.39k
    memset(cur, 0, sizeof(xsltKeyTable));
139
3.39k
    if (name != NULL)
140
3.39k
  cur->name = xmlStrdup(name);
141
3.39k
    if (nameURI != NULL)
142
1.87k
  cur->nameURI = xmlStrdup(nameURI);
143
3.39k
    cur->keys = xmlHashCreate(0);
144
3.39k
    return(cur);
145
3.39k
}
146
147
static void
148
795
xsltFreeNodeSetEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
149
795
    xmlXPathFreeNodeSet((xmlNodeSetPtr) payload);
150
795
}
151
152
/**
153
 * xsltFreeKeyTable:
154
 * @keyt:  an XSLT key table
155
 *
156
 * Free up the memory allocated by @keyt
157
 */
158
static void
159
3.39k
xsltFreeKeyTable(xsltKeyTablePtr keyt) {
160
3.39k
    if (keyt == NULL)
161
0
  return;
162
3.39k
    if (keyt->name != NULL)
163
3.39k
  xmlFree(keyt->name);
164
3.39k
    if (keyt->nameURI != NULL)
165
1.87k
  xmlFree(keyt->nameURI);
166
3.39k
    if (keyt->keys != NULL)
167
3.39k
  xmlHashFree(keyt->keys, xsltFreeNodeSetEntry);
168
3.39k
    memset(keyt, -1, sizeof(xsltKeyTable));
169
3.39k
    xmlFree(keyt);
170
3.39k
}
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
149k
xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
180
149k
    xsltKeyTablePtr cur;
181
182
152k
    while (keyt != NULL) {
183
3.39k
  cur = keyt;
184
3.39k
  keyt = keyt->next;
185
3.39k
  xsltFreeKeyTable(cur);
186
3.39k
    }
187
149k
}
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
227k
xsltFreeKeys(xsltStylesheetPtr style) {
204
227k
    if (style->keys)
205
1.50k
  xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
206
227k
}
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
661
skipString(const xmlChar *cur, int end) {
219
661
    xmlChar limit;
220
221
661
    if ((cur == NULL) || (end < 0)) return(-1);
222
661
    if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end];
223
0
    else return(end);
224
661
    end++;
225
4.62k
    while (cur[end] != 0) {
226
4.48k
        if (cur[end] == limit)
227
517
      return(end + 1);
228
3.96k
  end++;
229
3.96k
    }
230
144
    return(-1);
231
661
}
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
1.00k
skipPredicate(const xmlChar *cur, int end) {
244
1.00k
    int level = 0;
245
246
1.00k
    if ((cur == NULL) || (end < 0)) return(-1);
247
1.00k
    if (cur[end] != '[') return(end);
248
1.00k
    end++;
249
18.5k
    while (cur[end] != 0) {
250
18.0k
        if ((cur[end] == '\'') || (cur[end] == '"')) {
251
661
      end = skipString(cur, end);
252
661
      if (end <= 0)
253
144
          return(-1);
254
517
      continue;
255
17.3k
  } else if (cur[end] == '[') {
256
969
            level += 1;
257
16.4k
  } else if (cur[end] == ']') {
258
772
            if (level == 0)
259
336
          return(end + 1);
260
436
            level -= 1;
261
436
        }
262
17.0k
  end++;
263
17.0k
    }
264
521
    return(-1);
265
1.00k
}
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
6.65k
     const xmlChar *use, xmlNodePtr inst) {
284
6.65k
    xsltKeyDefPtr key;
285
6.65k
    xmlChar *pattern = NULL;
286
6.65k
    int current, end, start, i = 0;
287
288
6.65k
    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
6.65k
    key = xsltNewKeyDef(name, nameURI);
297
6.65k
    if (key == NULL)
298
1
        return(-1);
299
6.65k
    key->match = xmlStrdup(match);
300
6.65k
    key->use = xmlStrdup(use);
301
6.65k
    key->inst = inst;
302
6.65k
    key->nsList = xmlGetNsList(inst->doc, inst);
303
6.65k
    if (key->nsList != NULL) {
304
20.7k
        while (key->nsList[i] != NULL)
305
14.0k
      i++;
306
6.64k
    }
307
6.65k
    key->nsNr = i;
308
309
    /*
310
     * Split the | and register it as as many keys
311
     */
312
6.65k
    current = end = 0;
313
17.3k
    while (match[current] != 0) {
314
11.5k
  start = current;
315
17.5k
  while (xmlIsBlank_ch(match[current]))
316
5.99k
      current++;
317
11.5k
  end = current;
318
110k
  while ((match[end] != 0) && (match[end] != '|')) {
319
99.3k
      if (match[end] == '[') {
320
1.00k
          end = skipPredicate(match, end);
321
1.00k
    if (end <= 0) {
322
665
        xsltTransformError(NULL, style, inst,
323
665
            "xsl:key : 'match' pattern is malformed: %s",
324
665
            key->match);
325
665
        if (style != NULL) style->errors++;
326
665
        goto error;
327
665
    }
328
1.00k
      } else
329
98.3k
    end++;
330
99.3k
  }
331
10.8k
  if (current == end) {
332
114
      xsltTransformError(NULL, style, inst,
333
114
             "xsl:key : 'match' pattern is empty\n");
334
114
      if (style != NULL) style->errors++;
335
114
      goto error;
336
114
  }
337
10.7k
  if (match[start] != '/') {
338
7.94k
      pattern = xmlStrcat(pattern, (xmlChar *)"//");
339
7.94k
      if (pattern == NULL) {
340
4
    if (style != NULL) style->errors++;
341
4
    goto error;
342
4
      }
343
7.94k
  }
344
10.7k
  pattern = xmlStrncat(pattern, &match[start], end - start);
345
10.7k
  if (pattern == NULL) {
346
1
      if (style != NULL) style->errors++;
347
1
      goto error;
348
1
  }
349
350
10.7k
  if (match[end] == '|') {
351
4.95k
      pattern = xmlStrcat(pattern, (xmlChar *)"|");
352
4.95k
      end++;
353
4.95k
  }
354
10.7k
  current = end;
355
10.7k
    }
356
5.87k
    if (pattern == NULL) {
357
80
        xsltTransformError(NULL, style, inst,
358
80
                           "xsl:key : 'match' pattern is empty\n");
359
80
        if (style != NULL) style->errors++;
360
80
        goto error;
361
80
    }
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
5.79k
#ifdef XML_XPATH_NOVAR
375
5.79k
    key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR);
376
#else
377
    key->comp = xsltXPathCompile(style, pattern);
378
#endif
379
5.79k
    if (key->comp == NULL) {
380
809
  xsltTransformError(NULL, style, inst,
381
809
    "xsl:key : 'match' pattern compilation failed '%s'\n",
382
809
             pattern);
383
809
  if (style != NULL) style->errors++;
384
809
    }
385
5.79k
#ifdef XML_XPATH_NOVAR
386
5.79k
    key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR);
387
#else
388
    key->usecomp = xsltXPathCompile(style, use);
389
#endif
390
5.79k
    if (key->usecomp == NULL) {
391
297
  xsltTransformError(NULL, style, inst,
392
297
    "xsl:key : 'use' expression compilation failed '%s'\n",
393
297
             use);
394
297
  if (style != NULL) style->errors++;
395
297
    }
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
5.79k
    if (style->keys == NULL) {
403
1.50k
  style->keys = key;
404
4.28k
    } else {
405
4.28k
        xsltKeyDefPtr prev = style->keys;
406
407
30.8k
  while (prev->next != NULL)
408
26.6k
      prev = prev->next;
409
410
4.28k
  prev->next = key;
411
4.28k
    }
412
5.79k
    key->next = NULL;
413
5.79k
    key = NULL;
414
415
6.65k
error:
416
6.65k
    if (pattern != NULL)
417
5.86k
  xmlFree(pattern);
418
6.65k
    if (key != NULL)
419
864
        xsltFreeKeyDef(key);
420
6.65k
    return(0);
421
5.79k
}
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
8.20k
     const xmlChar *nameURI, const xmlChar *value) {
439
8.20k
    xmlNodeSetPtr ret;
440
8.20k
    xsltKeyTablePtr table;
441
8.20k
    int init_table = 0;
442
443
8.20k
    if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
444
8.11k
  (ctxt->document == NULL))
445
89
  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
8.11k
    if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) &&
456
1.19k
        (ctxt->keyInitLevel == 0)) {
457
        /*
458
   * If non-recursive behaviour, just try to initialize all keys
459
   */
460
1.19k
  if (xsltInitAllDocKeys(ctxt))
461
0
      return(NULL);
462
1.19k
    }
463
464
8.11k
retry:
465
8.11k
    table = (xsltKeyTablePtr) ctxt->document->keys;
466
12.5k
    while (table != NULL) {
467
5.09k
  if (((nameURI != NULL) == (table->nameURI != NULL)) &&
468
3.13k
      xmlStrEqual(table->name, name) &&
469
641
      xmlStrEqual(table->nameURI, nameURI))
470
641
  {
471
641
      ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
472
641
      return(ret);
473
641
  }
474
4.44k
  table = table->next;
475
4.44k
    }
476
477
7.47k
    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
7.47k
    return(NULL);
488
7.47k
}
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
4.66k
{
502
4.66k
    xsltStylesheetPtr style;
503
4.66k
    xsltKeyDefPtr keyd = NULL;
504
4.66k
    int found = 0;
505
506
#ifdef KEY_INIT_DEBUG
507
fprintf(stderr, "xsltInitDocKeyTable %s\n", name);
508
#endif
509
510
4.66k
    style = ctxt->style;
511
8.71k
    while (style != NULL) {
512
5.31k
  keyd = (xsltKeyDefPtr) style->keys;
513
49.6k
  while (keyd != NULL) {
514
45.5k
      if (((keyd->nameURI != NULL) ==
515
45.5k
     (nameURI != NULL)) &&
516
25.1k
    xmlStrEqual(keyd->name, name) &&
517
8.93k
    xmlStrEqual(keyd->nameURI, nameURI))
518
7.24k
      {
519
7.24k
    xsltInitCtxtKey(ctxt, ctxt->document, keyd);
520
7.24k
    if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
521
1.27k
        return(0);
522
5.96k
    found = 1;
523
5.96k
      }
524
44.2k
      keyd = keyd->next;
525
44.2k
  }
526
4.04k
  style = xsltNextImport(style);
527
4.04k
    }
528
3.39k
    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
3.39k
    return(0);
542
3.39k
}
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
1.27k
{
557
1.27k
    xsltStylesheetPtr style;
558
1.27k
    xsltKeyDefPtr keyd;
559
1.27k
    xsltKeyTablePtr table;
560
561
1.27k
    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
1.27k
    if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
570
0
  return(0);
571
572
573
    /*
574
    * TODO: This could be further optimized
575
    */
576
1.27k
    style = ctxt->style;
577
2.87k
    while (style) {
578
1.60k
  keyd = (xsltKeyDefPtr) style->keys;
579
7.93k
  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
6.33k
      table = (xsltKeyTablePtr) ctxt->document->keys;
588
21.5k
      while (table) {
589
16.8k
    if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
590
8.73k
        xmlStrEqual(keyd->name, table->name) &&
591
2.54k
        xmlStrEqual(keyd->nameURI, table->nameURI))
592
1.66k
    {
593
1.66k
        break;
594
1.66k
    }
595
15.1k
    table = table->next;
596
15.1k
      }
597
6.33k
      if (table == NULL) {
598
    /*
599
    * Keys with this QName have not been yet computed.
600
    */
601
4.66k
    xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI);
602
4.66k
      }
603
6.33k
      keyd = keyd->next;
604
6.33k
  }
605
1.60k
  style = xsltNextImport(style);
606
1.60k
    }
607
#ifdef KEY_INIT_DEBUG
608
fprintf(stderr, "xsltInitAllDocKeys: done\n");
609
#endif
610
1.27k
    return(0);
611
1.27k
}
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
7.24k
{
627
7.24k
    int i, len, k;
628
7.24k
    xmlNodeSetPtr matchList = NULL, keylist;
629
7.24k
    xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
630
7.24k
    xmlChar *str = NULL;
631
7.24k
    xsltKeyTablePtr table;
632
7.24k
    xmlNodePtr oldInst, cur;
633
7.24k
    xmlNodePtr oldContextNode;
634
7.24k
    xsltDocumentPtr oldDocInfo;
635
7.24k
    int oldXPPos, oldXPSize;
636
7.24k
    xmlNodePtr oldXPNode;
637
7.24k
    xmlDocPtr oldXPDoc;
638
7.24k
    int oldXPNsNr;
639
7.24k
    xmlNsPtr *oldXPNamespaces;
640
7.24k
    xmlXPathContextPtr xpctxt;
641
642
#ifdef KEY_INIT_DEBUG
643
fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel);
644
#endif
645
646
7.24k
    if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
647
0
  return(-1);
648
649
    /*
650
     * Detect recursive keys
651
     */
652
7.24k
    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
7.24k
    ctxt->keyInitLevel++;
665
666
7.24k
    xpctxt = ctxt->xpathCtxt;
667
7.24k
    idoc->nbKeysComputed++;
668
    /*
669
    * Save context state.
670
    */
671
7.24k
    oldInst = ctxt->inst;
672
7.24k
    oldDocInfo = ctxt->document;
673
7.24k
    oldContextNode = ctxt->node;
674
675
7.24k
    oldXPNode = xpctxt->node;
676
7.24k
    oldXPDoc = xpctxt->doc;
677
7.24k
    oldXPPos = xpctxt->proximityPosition;
678
7.24k
    oldXPSize = xpctxt->contextSize;
679
7.24k
    oldXPNsNr = xpctxt->nsNr;
680
7.24k
    oldXPNamespaces = xpctxt->namespaces;
681
682
    /*
683
    * Set up contexts.
684
    */
685
7.24k
    ctxt->document = idoc;
686
7.24k
    ctxt->node = (xmlNodePtr) idoc->doc;
687
7.24k
    ctxt->inst = keyDef->inst;
688
689
7.24k
    xpctxt->doc = idoc->doc;
690
7.24k
    xpctxt->node = (xmlNodePtr) idoc->doc;
691
    /* TODO : clarify the use of namespaces in keys evaluation */
692
7.24k
    xpctxt->namespaces = keyDef->nsList;
693
7.24k
    xpctxt->nsNr = keyDef->nsNr;
694
695
    /*
696
    * Evaluate the 'match' expression of the xsl:key.
697
    * TODO: The 'match' is a *pattern*.
698
    */
699
7.24k
    matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
700
7.24k
    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
453
  xsltTransformError(ctxt, NULL, keyDef->inst,
707
453
      "Failed to evaluate the 'match' expression.\n");
708
453
  ctxt->state = XSLT_STATE_STOPPED;
709
453
  goto error;
710
6.79k
    } else {
711
6.79k
  if (matchRes->type == XPATH_NODESET) {
712
6.73k
      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
6.73k
  } 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
54
      xsltTransformError(ctxt, NULL, keyDef->inst,
729
54
    "The 'match' expression did not evaluate to a node set.\n");
730
54
      ctxt->state = XSLT_STATE_STOPPED;
731
54
      goto error;
732
54
  }
733
6.79k
    }
734
6.73k
    if ((matchList == NULL) || (matchList->nodeNr <= 0))
735
1.67k
  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
5.05k
    table = (xsltKeyTablePtr) idoc->keys;
742
12.7k
    while (table != NULL) {
743
9.30k
        if (xmlStrEqual(table->name, keyDef->name) &&
744
2.71k
      (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
745
2.16k
       ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
746
1.61k
        (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
747
1.65k
      break;
748
7.64k
  table = table->next;
749
7.64k
    }
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
5.05k
    if (table == NULL) {
755
3.39k
        table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
756
3.39k
        if (table == NULL)
757
2
      goto error;
758
3.39k
        table->next = idoc->keys;
759
3.39k
        idoc->keys = table;
760
3.39k
    }
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
5.05k
    xpctxt->contextSize = 1;
769
5.05k
    xpctxt->proximityPosition = 1;
770
771
22.3k
    for (i = 0; i < matchList->nodeNr; i++) {
772
17.3k
  cur = matchList->nodeTab[i];
773
17.3k
  if (! IS_XSLT_REAL_NODE(cur))
774
342
      continue;
775
17.0k
        ctxt->node = cur;
776
17.0k
  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
17.0k
  if (useRes != NULL)
785
12.0k
      xmlXPathFreeObject(useRes);
786
17.0k
  useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
787
17.0k
  if (useRes == NULL) {
788
46
      xsltTransformError(ctxt, NULL, keyDef->inst,
789
46
    "Failed to evaluate the 'use' expression.\n");
790
46
      ctxt->state = XSLT_STATE_STOPPED;
791
46
      break;
792
46
  }
793
17.0k
  if (useRes->type == XPATH_NODESET) {
794
14.6k
      if ((useRes->nodesetval != NULL) &&
795
14.6k
    (useRes->nodesetval->nodeNr != 0))
796
3.03k
      {
797
3.03k
    len = useRes->nodesetval->nodeNr;
798
3.03k
    str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
799
11.6k
      } else {
800
11.6k
    continue;
801
11.6k
      }
802
14.6k
  } else {
803
2.31k
      len = 1;
804
2.31k
      if (useRes->type == XPATH_STRING) {
805
    /*
806
    * Consume the string value.
807
    */
808
143
    str = useRes->stringval;
809
143
    useRes->stringval = NULL;
810
2.17k
      } else {
811
2.17k
    str = xmlXPathCastToString(useRes);
812
2.17k
      }
813
2.31k
  }
814
  /*
815
  * Process all strings.
816
  */
817
5.35k
  k = 0;
818
74.1k
  while (1) {
819
74.1k
      if (str == NULL)
820
246
    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
73.8k
      keylist = xmlHashLookup(table->keys, str);
828
73.8k
      if (keylist == NULL) {
829
800
    keylist = xmlXPathNodeSetCreate(cur);
830
800
    if (keylist == NULL)
831
3
        goto error;
832
797
    if (xmlHashAddEntry(table->keys, str, keylist) < 0) {
833
2
                    xmlXPathFreeNodeSet(keylist);
834
2
                    goto error;
835
2
                }
836
73.0k
      } else {
837
    /*
838
    * TODO: How do we know if this function failed?
839
    */
840
73.0k
    xmlXPathNodeSetAdd(keylist, cur);
841
73.0k
      }
842
73.8k
            xsltSetSourceNodeFlags(ctxt, cur, XSLT_SOURCE_NODE_HAS_KEY);
843
73.8k
      xmlFree(str);
844
73.8k
      str = NULL;
845
846
74.1k
next_string:
847
74.1k
      k++;
848
74.1k
      if (k >= len)
849
5.35k
    break;
850
68.7k
      str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
851
68.7k
  }
852
5.35k
    }
853
854
6.72k
exit:
855
7.24k
error:
856
7.24k
    ctxt->keyInitLevel--;
857
    /*
858
    * Restore context state.
859
    */
860
7.24k
    xpctxt->node = oldXPNode;
861
7.24k
    xpctxt->doc = oldXPDoc;
862
7.24k
    xpctxt->nsNr = oldXPNsNr;
863
7.24k
    xpctxt->namespaces = oldXPNamespaces;
864
7.24k
    xpctxt->proximityPosition = oldXPPos;
865
7.24k
    xpctxt->contextSize = oldXPSize;
866
867
7.24k
    ctxt->node = oldContextNode;
868
7.24k
    ctxt->document = oldDocInfo;
869
7.24k
    ctxt->inst = oldInst;
870
871
7.24k
    if (str)
872
5
  xmlFree(str);
873
7.24k
    if (useRes != NULL)
874
4.91k
  xmlXPathFreeObject(useRes);
875
7.24k
    if (matchRes != NULL)
876
6.79k
  xmlXPathFreeObject(matchRes);
877
7.24k
    return(0);
878
6.72k
}
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
149k
xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
932
149k
    if (idoc != NULL)
933
149k
        xsltFreeKeyTableList(idoc->keys);
934
149k
}
935