Coverage Report

Created: 2025-06-22 06:55

/src/libxslt/libxslt/pattern.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * pattern.c: Implemetation of the template match compilation and lookup
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
/*
13
 * TODO: handle pathological cases like *[*[@a="b"]]
14
 * TODO: detect [number] at compilation, optimize accordingly
15
 */
16
17
#define IN_LIBXSLT
18
#include "libxslt.h"
19
20
#include <string.h>
21
22
#include <libxml/xmlmemory.h>
23
#include <libxml/tree.h>
24
#include <libxml/valid.h>
25
#include <libxml/hash.h>
26
#include <libxml/xmlerror.h>
27
#include <libxml/parserInternals.h>
28
#include <libxml/xpath.h>
29
#include "xslt.h"
30
#include "xsltInternals.h"
31
#include "xsltutils.h"
32
#include "imports.h"
33
#include "templates.h"
34
#include "keys.h"
35
#include "pattern.h"
36
#include "documents.h"
37
38
#ifdef WITH_XSLT_DEBUG
39
#define WITH_XSLT_DEBUG_PATTERN
40
#endif
41
42
/*
43
 * Types are private:
44
 */
45
46
typedef enum {
47
    XSLT_OP_END=0,
48
    XSLT_OP_ROOT,
49
    XSLT_OP_ELEM,
50
    XSLT_OP_ATTR,
51
    XSLT_OP_PARENT,
52
    XSLT_OP_ANCESTOR,
53
    XSLT_OP_ID,
54
    XSLT_OP_KEY,
55
    XSLT_OP_NS,
56
    XSLT_OP_ALL,
57
    XSLT_OP_PI,
58
    XSLT_OP_COMMENT,
59
    XSLT_OP_TEXT,
60
    XSLT_OP_NODE,
61
    XSLT_OP_PREDICATE
62
} xsltOp;
63
64
typedef enum {
65
    AXIS_CHILD=1,
66
    AXIS_ATTRIBUTE
67
} xsltAxis;
68
69
typedef struct _xsltStepState xsltStepState;
70
typedef xsltStepState *xsltStepStatePtr;
71
struct _xsltStepState {
72
    int step;
73
    xmlNodePtr node;
74
};
75
76
typedef struct _xsltStepStates xsltStepStates;
77
typedef xsltStepStates *xsltStepStatesPtr;
78
struct _xsltStepStates {
79
    int nbstates;
80
    int maxstates;
81
    xsltStepStatePtr states;
82
};
83
84
typedef struct _xsltStepOp xsltStepOp;
85
typedef xsltStepOp *xsltStepOpPtr;
86
struct _xsltStepOp {
87
    xsltOp op;
88
    xmlChar *value;
89
    xmlChar *value2;
90
    xmlChar *value3;
91
    xmlXPathCompExprPtr comp;
92
    /*
93
     * Optimisations for count
94
     */
95
    int        previousExtra;
96
    int        indexExtra;
97
    int        lenExtra;
98
};
99
100
struct _xsltCompMatch {
101
    struct _xsltCompMatch *next; /* siblings in the name hash */
102
    float priority;              /* the priority */
103
    const xmlChar *pattern;       /* the pattern */
104
    const xmlChar *mode;         /* the mode */
105
    const xmlChar *modeURI;      /* the mode URI */
106
    xsltTemplatePtr template;    /* the associated template */
107
    xmlNodePtr node;             /* the containing element */
108
109
    int direct;
110
    /* TODO fix the statically allocated size steps[] */
111
    int nbStep;
112
    int maxStep;
113
    xmlNsPtr *nsList;   /* the namespaces in scope */
114
    int nsNr;     /* the number of namespaces in scope */
115
    xsltStepOpPtr steps;        /* ops for computation */
116
};
117
118
typedef struct _xsltParserContext xsltParserContext;
119
typedef xsltParserContext *xsltParserContextPtr;
120
struct _xsltParserContext {
121
    xsltStylesheetPtr style;    /* the stylesheet */
122
    xsltTransformContextPtr ctxt; /* the transformation or NULL */
123
    const xmlChar *cur;     /* the current char being parsed */
124
    const xmlChar *base;    /* the full expression */
125
    xmlDocPtr      doc;     /* the source document */
126
    xmlNodePtr    elem;     /* the source element */
127
    int error;        /* error code */
128
    xsltCompMatchPtr comp;    /* the result */
129
};
130
131
/************************************************************************
132
 *                  *
133
 *      Type functions          *
134
 *                  *
135
 ************************************************************************/
136
137
/**
138
 * xsltNewCompMatch:
139
 *
140
 * Create a new XSLT CompMatch
141
 *
142
 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
143
 */
144
static xsltCompMatchPtr
145
0
xsltNewCompMatch(void) {
146
0
    xsltCompMatchPtr cur;
147
148
0
    cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
149
0
    if (cur == NULL) {
150
0
  xsltTransformError(NULL, NULL, NULL,
151
0
    "xsltNewCompMatch : out of memory error\n");
152
0
  return(NULL);
153
0
    }
154
0
    memset(cur, 0, sizeof(xsltCompMatch));
155
0
    cur->maxStep = 10;
156
0
    cur->nbStep = 0;
157
0
    cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) *
158
0
                                            cur->maxStep);
159
0
    if (cur->steps == NULL) {
160
0
  xsltTransformError(NULL, NULL, NULL,
161
0
    "xsltNewCompMatch : out of memory error\n");
162
0
  xmlFree(cur);
163
0
  return(NULL);
164
0
    }
165
0
    cur->nsNr = 0;
166
0
    cur->nsList = NULL;
167
0
    cur->direct = 0;
168
0
    return(cur);
169
0
}
170
171
/**
172
 * xsltFreeCompMatch:
173
 * @comp:  an XSLT comp
174
 *
175
 * Free up the memory allocated by @comp
176
 */
177
static void
178
0
xsltFreeCompMatch(xsltCompMatchPtr comp) {
179
0
    xsltStepOpPtr op;
180
0
    int i;
181
182
0
    if (comp == NULL)
183
0
  return;
184
0
    if (comp->pattern != NULL)
185
0
  xmlFree((xmlChar *)comp->pattern);
186
0
    if (comp->nsList != NULL)
187
0
  xmlFree(comp->nsList);
188
0
    for (i = 0;i < comp->nbStep;i++) {
189
0
  op = &comp->steps[i];
190
0
  if (op->value != NULL)
191
0
      xmlFree(op->value);
192
0
  if (op->value2 != NULL)
193
0
      xmlFree(op->value2);
194
0
  if (op->value3 != NULL)
195
0
      xmlFree(op->value3);
196
0
  if (op->comp != NULL)
197
0
      xmlXPathFreeCompExpr(op->comp);
198
0
    }
199
0
    xmlFree(comp->steps);
200
0
    memset(comp, -1, sizeof(xsltCompMatch));
201
0
    xmlFree(comp);
202
0
}
203
204
/**
205
 * xsltFreeCompMatchList:
206
 * @comp:  an XSLT comp list
207
 *
208
 * Free up the memory allocated by all the elements of @comp
209
 */
210
void
211
0
xsltFreeCompMatchList(xsltCompMatchPtr comp) {
212
0
    xsltCompMatchPtr cur;
213
214
0
    while (comp != NULL) {
215
0
  cur = comp;
216
0
  comp = comp->next;
217
0
  xsltFreeCompMatch(cur);
218
0
    }
219
0
}
220
221
static void
222
xsltFreeCompMatchListEntry(void *payload,
223
0
                           const xmlChar *name ATTRIBUTE_UNUSED) {
224
0
    xsltFreeCompMatchList((xsltCompMatchPtr) payload);
225
0
}
226
227
/**
228
 * xsltNormalizeCompSteps:
229
 * @payload: pointer to template hash table entry
230
 * @data: pointer to the stylesheet
231
 * @name: template match name
232
 *
233
 * This is a hashtable scanner function to normalize the compiled
234
 * steps of an imported stylesheet.
235
 */
236
void xsltNormalizeCompSteps(void *payload,
237
0
        void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
238
0
    xsltCompMatchPtr comp = payload;
239
0
    xsltStylesheetPtr style = data;
240
0
    int ix;
241
242
0
    for (ix = 0; ix < comp->nbStep; ix++) {
243
0
        comp->steps[ix].previousExtra += style->extrasNr;
244
0
        comp->steps[ix].indexExtra += style->extrasNr;
245
0
        comp->steps[ix].lenExtra += style->extrasNr;
246
0
    }
247
0
}
248
249
/**
250
 * xsltNewParserContext:
251
 * @style:  the stylesheet
252
 * @ctxt:  the transformation context, if done at run-time
253
 *
254
 * Create a new XSLT ParserContext
255
 *
256
 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
257
 */
258
static xsltParserContextPtr
259
0
xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
260
0
    xsltParserContextPtr cur;
261
262
0
    cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
263
0
    if (cur == NULL) {
264
0
  xsltTransformError(NULL, NULL, NULL,
265
0
    "xsltNewParserContext : malloc failed\n");
266
0
  return(NULL);
267
0
    }
268
0
    memset(cur, 0, sizeof(xsltParserContext));
269
0
    cur->style = style;
270
0
    cur->ctxt = ctxt;
271
0
    return(cur);
272
0
}
273
274
/**
275
 * xsltFreeParserContext:
276
 * @ctxt:  an XSLT parser context
277
 *
278
 * Free up the memory allocated by @ctxt
279
 */
280
static void
281
0
xsltFreeParserContext(xsltParserContextPtr ctxt) {
282
0
    if (ctxt == NULL)
283
0
  return;
284
0
    memset(ctxt, -1, sizeof(xsltParserContext));
285
0
    xmlFree(ctxt);
286
0
}
287
288
/**
289
 * xsltCompMatchAdd:
290
 * @comp:  the compiled match expression
291
 * @op:  an op
292
 * @value:  the first value
293
 * @value2:  the second value
294
 * @novar:  flag to set XML_XPATH_NOVAR
295
 *
296
 * Add an step to an XSLT Compiled Match
297
 *
298
 * Returns -1 in case of failure, 0 otherwise.
299
 */
300
static int
301
xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
302
                 xsltOp op, xmlChar * value, xmlChar * value2, int novar)
303
0
{
304
0
    if (comp->nbStep >= comp->maxStep) {
305
0
        xsltStepOpPtr tmp;
306
307
0
  tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
308
0
                                   sizeof(xsltStepOp));
309
0
  if (tmp == NULL) {
310
0
      xsltGenericError(xsltGenericErrorContext,
311
0
       "xsltCompMatchAdd: memory re-allocation failure.\n");
312
0
      if (ctxt->style != NULL)
313
0
    ctxt->style->errors++;
314
0
      return (-1);
315
0
  }
316
0
        comp->maxStep *= 2;
317
0
  comp->steps = tmp;
318
0
    }
319
0
    comp->steps[comp->nbStep].op = op;
320
0
    comp->steps[comp->nbStep].value = value;
321
0
    comp->steps[comp->nbStep].value2 = value2;
322
0
    comp->steps[comp->nbStep].value3 = NULL;
323
0
    comp->steps[comp->nbStep].comp = NULL;
324
0
    if (ctxt->ctxt != NULL) {
325
0
  comp->steps[comp->nbStep].previousExtra =
326
0
      xsltAllocateExtraCtxt(ctxt->ctxt);
327
0
  comp->steps[comp->nbStep].indexExtra =
328
0
      xsltAllocateExtraCtxt(ctxt->ctxt);
329
0
  comp->steps[comp->nbStep].lenExtra =
330
0
      xsltAllocateExtraCtxt(ctxt->ctxt);
331
0
    } else {
332
0
  comp->steps[comp->nbStep].previousExtra =
333
0
      xsltAllocateExtra(ctxt->style);
334
0
  comp->steps[comp->nbStep].indexExtra =
335
0
      xsltAllocateExtra(ctxt->style);
336
0
  comp->steps[comp->nbStep].lenExtra =
337
0
      xsltAllocateExtra(ctxt->style);
338
0
    }
339
0
    if (op == XSLT_OP_PREDICATE) {
340
0
        int flags = 0;
341
342
0
#ifdef XML_XPATH_NOVAR
343
0
  if (novar != 0)
344
0
      flags = XML_XPATH_NOVAR;
345
0
#endif
346
0
  comp->steps[comp->nbStep].comp = xsltXPathCompileFlags(ctxt->style,
347
0
                value, flags);
348
0
  if (comp->steps[comp->nbStep].comp == NULL) {
349
0
      xsltTransformError(NULL, ctxt->style, ctxt->elem,
350
0
        "Failed to compile predicate\n");
351
0
      if (ctxt->style != NULL)
352
0
    ctxt->style->errors++;
353
0
  }
354
0
    }
355
0
    comp->nbStep++;
356
0
    return (0);
357
0
}
358
359
/**
360
 * xsltSwapTopCompMatch:
361
 * @comp:  the compiled match expression
362
 *
363
 * reverse the two top steps.
364
 */
365
static void
366
0
xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
367
0
    int i;
368
0
    int j = comp->nbStep - 1;
369
370
0
    if (j > 0) {
371
0
  register xmlChar *tmp;
372
0
  register xsltOp op;
373
0
  register xmlXPathCompExprPtr expr;
374
0
  register int t;
375
0
  i = j - 1;
376
0
  tmp = comp->steps[i].value;
377
0
  comp->steps[i].value = comp->steps[j].value;
378
0
  comp->steps[j].value = tmp;
379
0
  tmp = comp->steps[i].value2;
380
0
  comp->steps[i].value2 = comp->steps[j].value2;
381
0
  comp->steps[j].value2 = tmp;
382
0
  tmp = comp->steps[i].value3;
383
0
  comp->steps[i].value3 = comp->steps[j].value3;
384
0
  comp->steps[j].value3 = tmp;
385
0
  op = comp->steps[i].op;
386
0
  comp->steps[i].op = comp->steps[j].op;
387
0
  comp->steps[j].op = op;
388
0
  expr = comp->steps[i].comp;
389
0
  comp->steps[i].comp = comp->steps[j].comp;
390
0
  comp->steps[j].comp = expr;
391
0
  t = comp->steps[i].previousExtra;
392
0
  comp->steps[i].previousExtra = comp->steps[j].previousExtra;
393
0
  comp->steps[j].previousExtra = t;
394
0
  t = comp->steps[i].indexExtra;
395
0
  comp->steps[i].indexExtra = comp->steps[j].indexExtra;
396
0
  comp->steps[j].indexExtra = t;
397
0
  t = comp->steps[i].lenExtra;
398
0
  comp->steps[i].lenExtra = comp->steps[j].lenExtra;
399
0
  comp->steps[j].lenExtra = t;
400
0
    }
401
0
}
402
403
/**
404
 * xsltReverseCompMatch:
405
 * @ctxt: the parser context
406
 * @comp:  the compiled match expression
407
 *
408
 * reverse all the stack of expressions
409
 */
410
static void
411
0
xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) {
412
0
    int i = 0;
413
0
    int j = comp->nbStep - 1;
414
415
0
    while (j > i) {
416
0
  register xmlChar *tmp;
417
0
  register xsltOp op;
418
0
  register xmlXPathCompExprPtr expr;
419
0
  register int t;
420
421
0
  tmp = comp->steps[i].value;
422
0
  comp->steps[i].value = comp->steps[j].value;
423
0
  comp->steps[j].value = tmp;
424
0
  tmp = comp->steps[i].value2;
425
0
  comp->steps[i].value2 = comp->steps[j].value2;
426
0
  comp->steps[j].value2 = tmp;
427
0
  tmp = comp->steps[i].value3;
428
0
  comp->steps[i].value3 = comp->steps[j].value3;
429
0
  comp->steps[j].value3 = tmp;
430
0
  op = comp->steps[i].op;
431
0
  comp->steps[i].op = comp->steps[j].op;
432
0
  comp->steps[j].op = op;
433
0
  expr = comp->steps[i].comp;
434
0
  comp->steps[i].comp = comp->steps[j].comp;
435
0
  comp->steps[j].comp = expr;
436
0
  t = comp->steps[i].previousExtra;
437
0
  comp->steps[i].previousExtra = comp->steps[j].previousExtra;
438
0
  comp->steps[j].previousExtra = t;
439
0
  t = comp->steps[i].indexExtra;
440
0
  comp->steps[i].indexExtra = comp->steps[j].indexExtra;
441
0
  comp->steps[j].indexExtra = t;
442
0
  t = comp->steps[i].lenExtra;
443
0
  comp->steps[i].lenExtra = comp->steps[j].lenExtra;
444
0
  comp->steps[j].lenExtra = t;
445
0
  j--;
446
0
  i++;
447
0
    }
448
0
    xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0);
449
450
    /*
451
     * Detect consecutive XSLT_OP_PREDICATE indicating a direct matching
452
     * should be done.
453
     */
454
0
    for (i = 0;i < comp->nbStep - 1;i++) {
455
0
        if ((comp->steps[i].op == XSLT_OP_PREDICATE) &&
456
0
      (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
457
458
0
      comp->direct = 1;
459
0
      if (comp->pattern[0] != '/') {
460
0
    xmlChar *query;
461
462
0
    query = xmlStrdup((const xmlChar *)"//");
463
0
    query = xmlStrcat(query, comp->pattern);
464
465
0
    xmlFree((xmlChar *) comp->pattern);
466
0
    comp->pattern = query;
467
0
      }
468
0
      break;
469
0
  }
470
0
    }
471
0
}
472
473
/************************************************************************
474
 *                  *
475
 *    The interpreter for the precompiled patterns    *
476
 *                  *
477
 ************************************************************************/
478
479
static int
480
xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
481
0
                 int step, xmlNodePtr node) {
482
0
    if (states->maxstates <= states->nbstates) {
483
0
        xsltStepState *tmp;
484
0
        int newMax = states->maxstates == 0 ? 4 : 2 * states->maxstates;
485
486
0
  tmp = (xsltStepStatePtr) xmlRealloc(states->states,
487
0
                newMax * sizeof(xsltStepState));
488
0
  if (tmp == NULL) {
489
0
      xsltGenericError(xsltGenericErrorContext,
490
0
       "xsltPatPushState: memory re-allocation failure.\n");
491
0
      ctxt->state = XSLT_STATE_STOPPED;
492
0
      return(-1);
493
0
  }
494
0
  states->states = tmp;
495
0
  states->maxstates = newMax;
496
0
    }
497
0
    states->states[states->nbstates].step = step;
498
0
    states->states[states->nbstates++].node = node;
499
#if 0
500
    fprintf(stderr, "Push: %d, %s\n", step, node->name);
501
#endif
502
0
    return(0);
503
0
}
504
505
static void
506
0
xmlXPathFreeObjectWrapper(void *obj) {
507
0
    xmlXPathFreeObject((xmlXPathObjectPtr) obj);
508
0
}
509
510
/**
511
 * xsltTestCompMatchDirect:
512
 * @ctxt:  a XSLT process context
513
 * @comp: the precompiled pattern
514
 * @node: a node
515
 * @nsList: the namespaces in scope
516
 * @nsNr: the number of namespaces in scope
517
 *
518
 * Test whether the node matches the pattern, do a direct evalutation
519
 * and not a step by step evaluation.
520
 *
521
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
522
 */
523
static int
524
xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
525
0
                  xmlNodePtr node, xmlNsPtr *nsList, int nsNr) {
526
0
    xsltStepOpPtr sel = NULL;
527
0
    xmlDocPtr prevdoc;
528
0
    xmlDocPtr doc;
529
0
    xmlXPathObjectPtr list;
530
0
    int ix, j;
531
0
    int nocache = 0;
532
0
    int isRVT;
533
534
0
    doc = node->doc;
535
0
    if (XSLT_IS_RES_TREE_FRAG(doc))
536
0
  isRVT = 1;
537
0
    else
538
0
  isRVT = 0;
539
0
    sel = &comp->steps[0]; /* store extra in first step arbitrarily */
540
541
0
    prevdoc = (xmlDocPtr)
542
0
  XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
543
0
    ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
544
0
    list = (xmlXPathObjectPtr)
545
0
  XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
546
547
0
    if ((list == NULL) || (prevdoc != doc)) {
548
0
  xmlXPathObjectPtr newlist;
549
0
  xmlNodePtr parent = node->parent;
550
0
  xmlDocPtr olddoc;
551
0
  xmlNodePtr oldnode;
552
0
  int oldNsNr, oldContextSize, oldProximityPosition;
553
0
  xmlNsPtr *oldNamespaces;
554
555
0
  oldnode = ctxt->xpathCtxt->node;
556
0
  olddoc = ctxt->xpathCtxt->doc;
557
0
  oldNsNr = ctxt->xpathCtxt->nsNr;
558
0
  oldNamespaces = ctxt->xpathCtxt->namespaces;
559
0
  oldContextSize = ctxt->xpathCtxt->contextSize;
560
0
  oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
561
0
  ctxt->xpathCtxt->node = node;
562
0
  ctxt->xpathCtxt->doc = doc;
563
0
  ctxt->xpathCtxt->namespaces = nsList;
564
0
  ctxt->xpathCtxt->nsNr = nsNr;
565
0
  newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
566
0
  ctxt->xpathCtxt->node = oldnode;
567
0
  ctxt->xpathCtxt->doc = olddoc;
568
0
  ctxt->xpathCtxt->namespaces = oldNamespaces;
569
0
  ctxt->xpathCtxt->nsNr = oldNsNr;
570
0
  ctxt->xpathCtxt->contextSize = oldContextSize;
571
0
  ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
572
0
  if (newlist == NULL)
573
0
      return(-1);
574
0
  if (newlist->type != XPATH_NODESET) {
575
0
      xmlXPathFreeObject(newlist);
576
0
      return(-1);
577
0
  }
578
0
  ix = 0;
579
580
0
  if ((parent == NULL) || (node->doc == NULL) || isRVT)
581
0
      nocache = 1;
582
583
0
  if (nocache == 0) {
584
0
      if (list != NULL)
585
0
    xmlXPathFreeObject(list);
586
0
      list = newlist;
587
588
0
      XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
589
0
    (void *) list;
590
0
      XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
591
0
    (void *) doc;
592
0
      XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
593
0
    0;
594
0
      XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
595
0
    xmlXPathFreeObjectWrapper;
596
0
  } else
597
0
      list = newlist;
598
0
    }
599
0
    if ((list->nodesetval == NULL) ||
600
0
  (list->nodesetval->nodeNr <= 0)) {
601
0
  if (nocache == 1)
602
0
      xmlXPathFreeObject(list);
603
0
  return(0);
604
0
    }
605
    /* TODO: store the index and use it for the scan */
606
0
    if (ix == 0) {
607
0
  for (j = 0;j < list->nodesetval->nodeNr;j++) {
608
0
      if (list->nodesetval->nodeTab[j] == node) {
609
0
    if (nocache == 1)
610
0
        xmlXPathFreeObject(list);
611
0
    return(1);
612
0
      }
613
0
  }
614
0
    } else {
615
0
    }
616
0
    if (nocache == 1)
617
0
  xmlXPathFreeObject(list);
618
0
    return(0);
619
0
}
620
621
/**
622
 * xsltTestStepMatch:
623
 * @ctxt:  a XSLT process context
624
 * @node: a node
625
 * @step:  the step
626
 *
627
 * Test whether the node matches the step.
628
 *
629
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
630
 */
631
static int
632
xsltTestStepMatch(xsltTransformContextPtr ctxt, xmlNodePtr node,
633
0
                  xsltStepOpPtr step) {
634
0
    switch (step->op) {
635
0
        case XSLT_OP_ROOT:
636
0
            if ((node->type == XML_DOCUMENT_NODE) ||
637
#ifdef LIBXML_DOCB_ENABLED
638
                (node->type == XML_DOCB_DOCUMENT_NODE) ||
639
#endif
640
0
                (node->type == XML_HTML_DOCUMENT_NODE))
641
0
                return(1);
642
0
            if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
643
0
                return(1);
644
0
            return(0);
645
0
        case XSLT_OP_ELEM:
646
0
            if (node->type != XML_ELEMENT_NODE)
647
0
                return(0);
648
0
            if (step->value == NULL)
649
0
                return(1);
650
0
            if (step->value[0] != node->name[0])
651
0
                return(0);
652
0
            if (!xmlStrEqual(step->value, node->name))
653
0
                return(0);
654
655
            /* Namespace test */
656
0
            if (node->ns == NULL) {
657
0
                if (step->value2 != NULL)
658
0
                    return(0);
659
0
            } else if (node->ns->href != NULL) {
660
0
                if (step->value2 == NULL)
661
0
                    return(0);
662
0
                if (!xmlStrEqual(step->value2, node->ns->href))
663
0
                    return(0);
664
0
            }
665
0
            return(1);
666
0
        case XSLT_OP_ATTR:
667
0
            if (node->type != XML_ATTRIBUTE_NODE)
668
0
                return(0);
669
0
            if (step->value != NULL) {
670
0
                if (step->value[0] != node->name[0])
671
0
                    return(0);
672
0
                if (!xmlStrEqual(step->value, node->name))
673
0
                    return(0);
674
0
            }
675
            /* Namespace test */
676
0
            if (node->ns == NULL) {
677
0
                if (step->value2 != NULL)
678
0
                    return(0);
679
0
            } else if (step->value2 != NULL) {
680
0
                if (!xmlStrEqual(step->value2, node->ns->href))
681
0
                    return(0);
682
0
            }
683
0
            return(1);
684
0
        case XSLT_OP_ID: {
685
            /* TODO Handle IDs decently, must be done differently */
686
0
            xmlAttrPtr id;
687
688
0
            if (node->type != XML_ELEMENT_NODE)
689
0
                return(0);
690
691
0
            id = xmlGetID(node->doc, step->value);
692
0
            if ((id == NULL) || (id->parent != node))
693
0
                return(0);
694
0
            break;
695
0
        }
696
0
        case XSLT_OP_KEY: {
697
0
            xmlNodeSetPtr list;
698
0
            int indx;
699
700
0
            list = xsltGetKey(ctxt, step->value,
701
0
                              step->value3, step->value2);
702
0
            if (list == NULL)
703
0
                return(0);
704
0
            for (indx = 0;indx < list->nodeNr;indx++)
705
0
                if (list->nodeTab[indx] == node)
706
0
                    break;
707
0
            if (indx >= list->nodeNr)
708
0
                return(0);
709
0
            break;
710
0
        }
711
0
        case XSLT_OP_NS:
712
0
            if (node->type != XML_ELEMENT_NODE)
713
0
                return(0);
714
0
            if (node->ns == NULL) {
715
0
                if (step->value != NULL)
716
0
                    return(0);
717
0
            } else if (node->ns->href != NULL) {
718
0
                if (step->value == NULL)
719
0
                    return(0);
720
0
                if (!xmlStrEqual(step->value, node->ns->href))
721
0
                    return(0);
722
0
            }
723
0
            break;
724
0
        case XSLT_OP_ALL:
725
0
            if (node->type != XML_ELEMENT_NODE)
726
0
                return(0);
727
0
            break;
728
0
        case XSLT_OP_PI:
729
0
            if (node->type != XML_PI_NODE)
730
0
                return(0);
731
0
            if (step->value != NULL) {
732
0
                if (!xmlStrEqual(step->value, node->name))
733
0
                    return(0);
734
0
            }
735
0
            break;
736
0
        case XSLT_OP_COMMENT:
737
0
            if (node->type != XML_COMMENT_NODE)
738
0
                return(0);
739
0
            break;
740
0
        case XSLT_OP_TEXT:
741
0
            if ((node->type != XML_TEXT_NODE) &&
742
0
                (node->type != XML_CDATA_SECTION_NODE))
743
0
                return(0);
744
0
            break;
745
0
        case XSLT_OP_NODE:
746
0
            switch (node->type) {
747
0
                case XML_ELEMENT_NODE:
748
0
                case XML_CDATA_SECTION_NODE:
749
0
                case XML_PI_NODE:
750
0
                case XML_COMMENT_NODE:
751
0
                case XML_TEXT_NODE:
752
0
                    break;
753
0
                default:
754
0
                    return(0);
755
0
            }
756
0
            break;
757
0
        default:
758
0
            xsltTransformError(ctxt, NULL, node,
759
0
                    "xsltTestStepMatch: unexpected step op %d\n",
760
0
                    step->op);
761
0
            return(-1);
762
0
    }
763
764
0
    return(1);
765
0
}
766
767
/**
768
 * xsltTestPredicateMatch:
769
 * @ctxt: a XSLT process context
770
 * @comp: the precompiled pattern
771
 * @node: a node
772
 * @step: the predicate step
773
 * @sel:  the previous step
774
 *
775
 * Test whether the node matches the predicate
776
 *
777
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
778
 */
779
static int
780
xsltTestPredicateMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
781
                       xmlNodePtr node, xsltStepOpPtr step,
782
0
                       xsltStepOpPtr sel) {
783
0
    xmlNodePtr oldNode;
784
0
    xmlDocPtr doc;
785
0
    int oldCS, oldCP;
786
0
    int pos = 0, len = 0;
787
0
    int isRVT;
788
0
    int match;
789
790
0
    if (step->value == NULL)
791
0
        return(0);
792
0
    if (step->comp == NULL)
793
0
        return(0);
794
0
    if (sel == NULL)
795
0
        return(0);
796
797
0
    doc = node->doc;
798
0
    if (XSLT_IS_RES_TREE_FRAG(doc))
799
0
        isRVT = 1;
800
0
    else
801
0
        isRVT = 0;
802
803
    /*
804
     * Recompute contextSize and proximityPosition.
805
     *
806
     * This could be improved in the following ways:
807
     *
808
     * - Skip recomputation if predicates don't use position() or last()
809
     * - Keep data for multiple parents. This would require a hash table
810
     *   or an unused member in xmlNode.
811
     * - Store node test results in a bitmap to avoid computing them twice.
812
     */
813
0
    oldCS = ctxt->xpathCtxt->contextSize;
814
0
    oldCP = ctxt->xpathCtxt->proximityPosition;
815
0
    {
816
0
        xmlNodePtr previous;
817
0
        int nocache = 0;
818
819
0
        previous = (xmlNodePtr)
820
0
            XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
821
0
        if ((previous != NULL) &&
822
0
            (previous->parent == node->parent)) {
823
            /*
824
             * just walk back to adjust the index
825
             */
826
0
            int indx = 0;
827
0
            xmlNodePtr sibling = node;
828
829
0
            while (sibling != NULL) {
830
0
                if (sibling == previous)
831
0
                    break;
832
0
                if (xsltTestStepMatch(ctxt, sibling, sel))
833
0
                    indx++;
834
0
                sibling = sibling->prev;
835
0
            }
836
0
            if (sibling == NULL) {
837
                /* hum going backward in document order ... */
838
0
                indx = 0;
839
0
                sibling = node;
840
0
                while (sibling != NULL) {
841
0
                    if (sibling == previous)
842
0
                        break;
843
0
                    if (xsltTestStepMatch(ctxt, sibling, sel))
844
0
                        indx--;
845
0
                    sibling = sibling->next;
846
0
                }
847
0
            }
848
0
            if (sibling != NULL) {
849
0
                pos = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) + indx;
850
                /*
851
                 * If the node is in a Value Tree we need to
852
                 * save len, but cannot cache the node!
853
                 * (bugs 153137 and 158840)
854
                 */
855
0
                if (node->doc != NULL) {
856
0
                    len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival);
857
0
                    if (!isRVT) {
858
0
                        XSLT_RUNTIME_EXTRA(ctxt,
859
0
                            sel->previousExtra, ptr) = node;
860
0
                        XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
861
0
                    }
862
0
                }
863
0
            } else
864
0
                pos = 0;
865
0
        } else {
866
            /*
867
             * recompute the index
868
             */
869
0
            xmlNodePtr parent = node->parent;
870
0
            xmlNodePtr siblings = NULL;
871
872
0
            if (parent) siblings = parent->children;
873
874
0
            while (siblings != NULL) {
875
0
                if (siblings == node) {
876
0
                    len++;
877
0
                    pos = len;
878
0
                } else if (xsltTestStepMatch(ctxt, siblings, sel)) {
879
0
                    len++;
880
0
                }
881
0
                siblings = siblings->next;
882
0
            }
883
0
            if ((parent == NULL) || (node->doc == NULL))
884
0
                nocache = 1;
885
0
            else {
886
0
                while (parent->parent != NULL)
887
0
                    parent = parent->parent;
888
0
                if (((parent->type != XML_DOCUMENT_NODE) &&
889
0
                     (parent->type != XML_HTML_DOCUMENT_NODE)) ||
890
0
                     (parent != (xmlNodePtr) node->doc))
891
0
                    nocache = 1;
892
0
            }
893
0
        }
894
0
        if (pos != 0) {
895
0
            ctxt->xpathCtxt->contextSize = len;
896
0
            ctxt->xpathCtxt->proximityPosition = pos;
897
            /*
898
             * If the node is in a Value Tree we cannot
899
             * cache it !
900
             */
901
0
            if ((!isRVT) && (node->doc != NULL) &&
902
0
                (nocache == 0)) {
903
0
                XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
904
0
                XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
905
0
                XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len;
906
0
            }
907
0
        }
908
0
    }
909
910
0
    oldNode = ctxt->node;
911
0
    ctxt->node = node;
912
913
0
    match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr);
914
915
0
    if (pos != 0) {
916
0
        ctxt->xpathCtxt->contextSize = oldCS;
917
0
        ctxt->xpathCtxt->proximityPosition = oldCP;
918
0
    }
919
0
    ctxt->node = oldNode;
920
921
0
    return match;
922
0
}
923
924
/**
925
 * xsltTestCompMatch:
926
 * @ctxt:  a XSLT process context
927
 * @comp: the precompiled pattern
928
 * @node: a node
929
 * @mode:  the mode name or NULL
930
 * @modeURI:  the mode URI or NULL
931
 *
932
 * Test whether the node matches the pattern
933
 *
934
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
935
 */
936
static int
937
xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
938
            xmlNodePtr matchNode, const xmlChar *mode,
939
0
      const xmlChar *modeURI) {
940
0
    int i;
941
0
    int found = 0;
942
0
    xmlNodePtr node = matchNode;
943
0
    xmlNodePtr oldInst;
944
0
    xsltStepOpPtr step, sel = NULL;
945
0
    xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
946
947
0
    if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
948
0
  xsltTransformError(ctxt, NULL, node,
949
0
    "xsltTestCompMatch: null arg\n");
950
0
        return(-1);
951
0
    }
952
0
    if (mode != NULL) {
953
0
  if (comp->mode == NULL)
954
0
      return(0);
955
  /*
956
   * both mode strings must be interned on the stylesheet dictionary
957
   */
958
0
  if (comp->mode != mode)
959
0
      return(0);
960
0
    } else {
961
0
  if (comp->mode != NULL)
962
0
      return(0);
963
0
    }
964
0
    if (modeURI != NULL) {
965
0
  if (comp->modeURI == NULL)
966
0
      return(0);
967
  /*
968
   * both modeURI strings must be interned on the stylesheet dictionary
969
   */
970
0
  if (comp->modeURI != modeURI)
971
0
      return(0);
972
0
    } else {
973
0
  if (comp->modeURI != NULL)
974
0
      return(0);
975
0
    }
976
977
    /* Some XPath functions rely on inst being set correctly. */
978
0
    oldInst = ctxt->inst;
979
0
    ctxt->inst = comp->node;
980
981
0
    i = 0;
982
0
restart:
983
0
    for (;i < comp->nbStep;i++) {
984
0
  step = &comp->steps[i];
985
0
  if (step->op != XSLT_OP_PREDICATE)
986
0
      sel = step;
987
0
  switch (step->op) {
988
0
            case XSLT_OP_END:
989
0
    goto found;
990
0
            case XSLT_OP_PARENT:
991
0
    if ((node->type == XML_DOCUMENT_NODE) ||
992
0
        (node->type == XML_HTML_DOCUMENT_NODE) ||
993
#ifdef LIBXML_DOCB_ENABLED
994
        (node->type == XML_DOCB_DOCUMENT_NODE) ||
995
#endif
996
0
        (node->type == XML_NAMESPACE_DECL))
997
0
        goto rollback;
998
0
    node = node->parent;
999
0
    if (node == NULL)
1000
0
        goto rollback;
1001
0
    if (step->value == NULL)
1002
0
        continue;
1003
0
    if (step->value[0] != node->name[0])
1004
0
        goto rollback;
1005
0
    if (!xmlStrEqual(step->value, node->name))
1006
0
        goto rollback;
1007
    /* Namespace test */
1008
0
    if (node->ns == NULL) {
1009
0
        if (step->value2 != NULL)
1010
0
      goto rollback;
1011
0
    } else if (node->ns->href != NULL) {
1012
0
        if (step->value2 == NULL)
1013
0
      goto rollback;
1014
0
        if (!xmlStrEqual(step->value2, node->ns->href))
1015
0
      goto rollback;
1016
0
    }
1017
0
    continue;
1018
0
            case XSLT_OP_ANCESTOR:
1019
    /* TODO: implement coalescing of ANCESTOR/NODE ops */
1020
0
    if (step->value == NULL) {
1021
0
        step = &comp->steps[i+1];
1022
0
        if (step->op == XSLT_OP_ROOT)
1023
0
      goto found;
1024
        /* added NS, ID and KEY as a result of bug 168208 */
1025
0
        if ((step->op != XSLT_OP_ELEM) &&
1026
0
      (step->op != XSLT_OP_ALL) &&
1027
0
      (step->op != XSLT_OP_NS) &&
1028
0
      (step->op != XSLT_OP_ID) &&
1029
0
      (step->op != XSLT_OP_KEY))
1030
0
      goto rollback;
1031
0
    }
1032
0
    if (node == NULL)
1033
0
        goto rollback;
1034
0
    if ((node->type == XML_DOCUMENT_NODE) ||
1035
0
        (node->type == XML_HTML_DOCUMENT_NODE) ||
1036
#ifdef LIBXML_DOCB_ENABLED
1037
        (node->type == XML_DOCB_DOCUMENT_NODE) ||
1038
#endif
1039
0
        (node->type == XML_NAMESPACE_DECL))
1040
0
        goto rollback;
1041
0
    node = node->parent;
1042
0
    if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
1043
0
        xsltPatPushState(ctxt, &states, i, node);
1044
0
        continue;
1045
0
    }
1046
0
    i++;
1047
0
                sel = step;
1048
0
    if (step->value == NULL) {
1049
0
        xsltPatPushState(ctxt, &states, i - 1, node);
1050
0
        continue;
1051
0
    }
1052
0
    while (node != NULL) {
1053
0
        if ((node->type == XML_ELEMENT_NODE) &&
1054
0
      (step->value[0] == node->name[0]) &&
1055
0
      (xmlStrEqual(step->value, node->name))) {
1056
      /* Namespace test */
1057
0
      if (node->ns == NULL) {
1058
0
          if (step->value2 == NULL)
1059
0
        break;
1060
0
      } else if (node->ns->href != NULL) {
1061
0
          if ((step->value2 != NULL) &&
1062
0
              (xmlStrEqual(step->value2, node->ns->href)))
1063
0
        break;
1064
0
      }
1065
0
        }
1066
0
        node = node->parent;
1067
0
    }
1068
0
    if (node == NULL)
1069
0
        goto rollback;
1070
0
    xsltPatPushState(ctxt, &states, i - 1, node);
1071
0
    continue;
1072
0
      case XSLT_OP_PREDICATE: {
1073
    /*
1074
     * When there is cascading XSLT_OP_PREDICATE or a predicate
1075
     * after an op which hasn't been optimized yet, then use a
1076
     * direct computation approach. It's not done directly
1077
     * at the beginning of the routine to filter out as much
1078
     * as possible this costly computation.
1079
     */
1080
0
    if (comp->direct) {
1081
0
        found = xsltTestCompMatchDirect(ctxt, comp, matchNode,
1082
0
                comp->nsList, comp->nsNr);
1083
0
                    goto exit;
1084
0
    }
1085
1086
0
    if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel))
1087
0
        goto rollback;
1088
1089
0
    break;
1090
0
      }
1091
0
            default:
1092
0
                if (xsltTestStepMatch(ctxt, node, step) != 1)
1093
0
                    goto rollback;
1094
0
    break;
1095
0
  }
1096
0
    }
1097
0
found:
1098
0
    found = 1;
1099
0
exit:
1100
0
    ctxt->inst = oldInst;
1101
0
    if (states.states != NULL) {
1102
        /* Free the rollback states */
1103
0
  xmlFree(states.states);
1104
0
    }
1105
0
    return found;
1106
0
rollback:
1107
    /* got an error try to rollback */
1108
0
    if (states.states == NULL || states.nbstates <= 0) {
1109
0
        found = 0;
1110
0
  goto exit;
1111
0
    }
1112
0
    states.nbstates--;
1113
0
    i = states.states[states.nbstates].step;
1114
0
    node = states.states[states.nbstates].node;
1115
#if 0
1116
    fprintf(stderr, "Pop: %d, %s\n", i, node->name);
1117
#endif
1118
0
    goto restart;
1119
0
}
1120
1121
/**
1122
 * xsltTestCompMatchList:
1123
 * @ctxt:  a XSLT process context
1124
 * @node: a node
1125
 * @comp: the precompiled pattern list
1126
 *
1127
 * Test whether the node matches one of the patterns in the list
1128
 *
1129
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1130
 */
1131
int
1132
xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
1133
0
                xsltCompMatchPtr comp) {
1134
0
    int ret;
1135
1136
0
    if ((ctxt == NULL) || (node == NULL))
1137
0
  return(-1);
1138
0
    while (comp != NULL) {
1139
0
  ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
1140
0
  if (ret == 1)
1141
0
      return(1);
1142
0
  comp = comp->next;
1143
0
    }
1144
0
    return(0);
1145
0
}
1146
1147
/**
1148
 * xsltCompMatchClearCache:
1149
 * @ctxt:  a XSLT process context
1150
 * @comp: the precompiled pattern list
1151
 *
1152
 * Clear pattern match cache.
1153
 */
1154
void
1155
0
xsltCompMatchClearCache(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp) {
1156
0
    xsltStepOpPtr sel;
1157
0
    xmlXPathObjectPtr list;
1158
1159
0
    if ((ctxt == NULL) || (comp == NULL))
1160
0
        return;
1161
1162
0
    sel = &comp->steps[0];
1163
0
    list = (xmlXPathObjectPtr) XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
1164
1165
0
    if (list != NULL) {
1166
0
        xmlXPathFreeObject(list);
1167
1168
0
        XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = NULL;
1169
0
        XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = NULL;
1170
0
        XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = 0;
1171
0
        XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = NULL;
1172
0
    }
1173
0
}
1174
1175
/************************************************************************
1176
 *                  *
1177
 *      Dedicated parser for templates      *
1178
 *                  *
1179
 ************************************************************************/
1180
1181
0
#define CUR (*ctxt->cur)
1182
#define SKIP(val) ctxt->cur += (val)
1183
0
#define NXT(val) ctxt->cur[(val)]
1184
0
#define CUR_PTR ctxt->cur
1185
1186
#define SKIP_BLANKS             \
1187
0
    while (xmlIsBlank_ch(CUR)) NEXT
1188
1189
#define CURRENT (*ctxt->cur)
1190
0
#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
1191
1192
1193
#define PUSH(op, val, val2, novar)            \
1194
0
    if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1195
1196
#define SWAP()            \
1197
0
    xsltSwapTopCompMatch(ctxt->comp);
1198
1199
#define XSLT_ERROR(X)             \
1200
    { xsltError(ctxt, __FILE__, __LINE__, X);     \
1201
      ctxt->error = (X); return; }
1202
1203
#define XSLT_ERROR0(X)              \
1204
    { xsltError(ctxt, __FILE__, __LINE__, X);     \
1205
      ctxt->error = (X); return(0); }
1206
1207
/**
1208
 * xsltScanLiteral:
1209
 * @ctxt:  the XPath Parser context
1210
 *
1211
 * Parse an XPath Litteral:
1212
 *
1213
 * [29] Literal ::= '"' [^"]* '"'
1214
 *                | "'" [^']* "'"
1215
 *
1216
 * Returns the Literal parsed or NULL
1217
 */
1218
1219
static xmlChar *
1220
0
xsltScanLiteral(xsltParserContextPtr ctxt) {
1221
0
    const xmlChar *q, *cur;
1222
0
    xmlChar *ret = NULL;
1223
0
    int val, len;
1224
1225
0
    SKIP_BLANKS;
1226
0
    if (CUR == '"') {
1227
0
        NEXT;
1228
0
  cur = q = CUR_PTR;
1229
0
  val = xsltGetUTF8CharZ(cur, &len);
1230
0
  while ((xmlIsCharQ(val)) && (val != '"')) {
1231
0
      cur += len;
1232
0
      val = xsltGetUTF8CharZ(cur, &len);
1233
0
  }
1234
0
  if (!xmlIsCharQ(val)) {
1235
0
      ctxt->error = 1;
1236
0
      return(NULL);
1237
0
  } else {
1238
0
      ret = xmlStrndup(q, cur - q);
1239
0
        }
1240
0
  cur += len;
1241
0
  CUR_PTR = cur;
1242
0
    } else if (CUR == '\'') {
1243
0
        NEXT;
1244
0
  cur = q = CUR_PTR;
1245
0
  val = xsltGetUTF8CharZ(cur, &len);
1246
0
  while ((xmlIsCharQ(val)) && (val != '\'')) {
1247
0
      cur += len;
1248
0
      val = xsltGetUTF8CharZ(cur, &len);
1249
0
  }
1250
0
  if (!xmlIsCharQ(val)) {
1251
0
      ctxt->error = 1;
1252
0
      return(NULL);
1253
0
  } else {
1254
0
      ret = xmlStrndup(q, cur - q);
1255
0
        }
1256
0
  cur += len;
1257
0
  CUR_PTR = cur;
1258
0
    } else {
1259
0
  ctxt->error = 1;
1260
0
  return(NULL);
1261
0
    }
1262
0
    return(ret);
1263
0
}
1264
1265
/**
1266
 * xsltScanNCName:
1267
 * @ctxt:  the XPath Parser context
1268
 *
1269
 * Parses a non qualified name
1270
 *
1271
 * Returns the Name parsed or NULL
1272
 */
1273
1274
static xmlChar *
1275
0
xsltScanNCName(xsltParserContextPtr ctxt) {
1276
0
    const xmlChar *q, *cur;
1277
0
    xmlChar *ret = NULL;
1278
0
    int val, len;
1279
1280
0
    SKIP_BLANKS;
1281
1282
0
    cur = q = CUR_PTR;
1283
0
    val = xsltGetUTF8CharZ(cur, &len);
1284
0
    if (!xmlIsBaseCharQ(val) && !xmlIsIdeographicQ(val) && (val != '_'))
1285
0
  return(NULL);
1286
1287
0
    while (xmlIsBaseCharQ(val) || xmlIsIdeographicQ(val) ||
1288
0
           xmlIsDigitQ(val) ||
1289
0
           (val == '.') || (val == '-') ||
1290
0
     (val == '_') ||
1291
0
     xmlIsCombiningQ(val) ||
1292
0
     xmlIsExtenderQ(val)) {
1293
0
  cur += len;
1294
0
  val = xsltGetUTF8CharZ(cur, &len);
1295
0
    }
1296
0
    ret = xmlStrndup(q, cur - q);
1297
0
    CUR_PTR = cur;
1298
0
    return(ret);
1299
0
}
1300
1301
/*
1302
 * xsltCompileIdKeyPattern:
1303
 * @ctxt:  the compilation context
1304
 * @name:  a preparsed name
1305
 * @aid:  whether id/key are allowed there
1306
 * @novar:  flag to prohibit xslt var
1307
 *
1308
 * Compile the XSLT LocationIdKeyPattern
1309
 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1310
 *                    | 'key' '(' Literal ',' Literal ')'
1311
 *
1312
 * also handle NodeType and PI from:
1313
 *
1314
 * [7]  NodeTest ::= NameTest
1315
 *                 | NodeType '(' ')'
1316
 *                 | 'processing-instruction' '(' Literal ')'
1317
 */
1318
static void
1319
xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
1320
0
    int aid, int novar, xsltAxis axis) {
1321
0
    xmlChar *lit = NULL;
1322
0
    xmlChar *lit2 = NULL;
1323
1324
0
    if (CUR != '(') {
1325
0
  xsltTransformError(NULL, NULL, NULL,
1326
0
    "xsltCompileIdKeyPattern : ( expected\n");
1327
0
  ctxt->error = 1;
1328
0
  return;
1329
0
    }
1330
0
    if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
1331
0
  if (axis != 0) {
1332
0
      xsltTransformError(NULL, NULL, NULL,
1333
0
        "xsltCompileIdKeyPattern : NodeTest expected\n");
1334
0
      ctxt->error = 1;
1335
0
      return;
1336
0
  }
1337
0
  NEXT;
1338
0
  SKIP_BLANKS;
1339
0
        lit = xsltScanLiteral(ctxt);
1340
0
  if (ctxt->error) {
1341
0
      xsltTransformError(NULL, NULL, NULL,
1342
0
        "xsltCompileIdKeyPattern : Literal expected\n");
1343
0
      xmlFree(lit);
1344
0
      return;
1345
0
  }
1346
0
  SKIP_BLANKS;
1347
0
  if (CUR != ')') {
1348
0
      xsltTransformError(NULL, NULL, NULL,
1349
0
        "xsltCompileIdKeyPattern : ) expected\n");
1350
0
      xmlFree(lit);
1351
0
      ctxt->error = 1;
1352
0
      return;
1353
0
  }
1354
0
  NEXT;
1355
0
  PUSH(XSLT_OP_ID, lit, NULL, novar);
1356
0
  lit = NULL;
1357
0
    } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
1358
0
  if (axis != 0) {
1359
0
      xsltTransformError(NULL, NULL, NULL,
1360
0
        "xsltCompileIdKeyPattern : NodeTest expected\n");
1361
0
      ctxt->error = 1;
1362
0
      return;
1363
0
  }
1364
0
  NEXT;
1365
0
  SKIP_BLANKS;
1366
0
        lit = xsltScanLiteral(ctxt);
1367
0
  if (ctxt->error) {
1368
0
      xsltTransformError(NULL, NULL, NULL,
1369
0
        "xsltCompileIdKeyPattern : Literal expected\n");
1370
0
      xmlFree(lit);
1371
0
      return;
1372
0
  }
1373
0
  SKIP_BLANKS;
1374
0
  if (CUR != ',') {
1375
0
      xsltTransformError(NULL, NULL, NULL,
1376
0
        "xsltCompileIdKeyPattern : , expected\n");
1377
0
      xmlFree(lit);
1378
0
      ctxt->error = 1;
1379
0
      return;
1380
0
  }
1381
0
  NEXT;
1382
0
  SKIP_BLANKS;
1383
0
        lit2 = xsltScanLiteral(ctxt);
1384
0
  if (ctxt->error) {
1385
0
      xsltTransformError(NULL, NULL, NULL,
1386
0
        "xsltCompileIdKeyPattern : Literal expected\n");
1387
0
      xmlFree(lit);
1388
0
      return;
1389
0
  }
1390
0
  SKIP_BLANKS;
1391
0
  if (CUR != ')') {
1392
0
      xsltTransformError(NULL, NULL, NULL,
1393
0
        "xsltCompileIdKeyPattern : ) expected\n");
1394
0
      xmlFree(lit);
1395
0
      xmlFree(lit2);
1396
0
      ctxt->error = 1;
1397
0
      return;
1398
0
  }
1399
0
  NEXT;
1400
  /* URGENT TODO: support namespace in keys */
1401
0
  PUSH(XSLT_OP_KEY, lit, lit2, novar);
1402
0
  lit = NULL;
1403
0
  lit2 = NULL;
1404
0
    } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
1405
0
  NEXT;
1406
0
  SKIP_BLANKS;
1407
0
  if (CUR != ')') {
1408
0
      lit = xsltScanLiteral(ctxt);
1409
0
      if (ctxt->error) {
1410
0
    xsltTransformError(NULL, NULL, NULL,
1411
0
      "xsltCompileIdKeyPattern : Literal expected\n");
1412
0
                xmlFree(lit);
1413
0
    return;
1414
0
      }
1415
0
      SKIP_BLANKS;
1416
0
      if (CUR != ')') {
1417
0
    xsltTransformError(NULL, NULL, NULL,
1418
0
      "xsltCompileIdKeyPattern : ) expected\n");
1419
0
    ctxt->error = 1;
1420
0
                xmlFree(lit);
1421
0
    return;
1422
0
      }
1423
0
  }
1424
0
  NEXT;
1425
0
  PUSH(XSLT_OP_PI, lit, NULL, novar);
1426
0
  lit = NULL;
1427
0
    } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
1428
0
  NEXT;
1429
0
  SKIP_BLANKS;
1430
0
  if (CUR != ')') {
1431
0
      xsltTransformError(NULL, NULL, NULL,
1432
0
        "xsltCompileIdKeyPattern : ) expected\n");
1433
0
      ctxt->error = 1;
1434
0
      return;
1435
0
  }
1436
0
  NEXT;
1437
0
  PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
1438
0
    } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
1439
0
  NEXT;
1440
0
  SKIP_BLANKS;
1441
0
  if (CUR != ')') {
1442
0
      xsltTransformError(NULL, NULL, NULL,
1443
0
        "xsltCompileIdKeyPattern : ) expected\n");
1444
0
      ctxt->error = 1;
1445
0
      return;
1446
0
  }
1447
0
  NEXT;
1448
0
  PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
1449
0
    } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
1450
0
  NEXT;
1451
0
  SKIP_BLANKS;
1452
0
  if (CUR != ')') {
1453
0
      xsltTransformError(NULL, NULL, NULL,
1454
0
        "xsltCompileIdKeyPattern : ) expected\n");
1455
0
      ctxt->error = 1;
1456
0
      return;
1457
0
  }
1458
0
  NEXT;
1459
0
  if (axis == AXIS_ATTRIBUTE) {
1460
0
      PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1461
0
  }
1462
0
  else {
1463
0
      PUSH(XSLT_OP_NODE, NULL, NULL, novar);
1464
0
  }
1465
0
    } else if (aid) {
1466
0
  xsltTransformError(NULL, NULL, NULL,
1467
0
      "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1468
0
  ctxt->error = 1;
1469
0
  return;
1470
0
    } else {
1471
0
  xsltTransformError(NULL, NULL, NULL,
1472
0
      "xsltCompileIdKeyPattern : node type\n");
1473
0
  ctxt->error = 1;
1474
0
  return;
1475
0
    }
1476
0
error:
1477
0
    return;
1478
0
}
1479
1480
/**
1481
 * xsltCompileStepPattern:
1482
 * @ctxt:  the compilation context
1483
 * @token:  a posible precompiled name
1484
 * @novar: flag to prohibit xslt variables from pattern
1485
 *
1486
 * Compile the XSLT StepPattern and generates a precompiled
1487
 * form suitable for fast matching.
1488
 *
1489
 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
1490
 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1491
 *                                     | ('child' | 'attribute') '::'
1492
 * from XPath
1493
 * [7]  NodeTest ::= NameTest
1494
 *                 | NodeType '(' ')'
1495
 *                 | 'processing-instruction' '(' Literal ')'
1496
 * [8] Predicate ::= '[' PredicateExpr ']'
1497
 * [9] PredicateExpr ::= Expr
1498
 * [13] AbbreviatedAxisSpecifier ::= '@'?
1499
 * [37] NameTest ::= '*' | NCName ':' '*' | QName
1500
 */
1501
1502
static void
1503
0
xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1504
0
    xmlChar *name = NULL;
1505
0
    const xmlChar *URI = NULL;
1506
0
    xmlChar *URL = NULL;
1507
0
    xmlChar *ret = NULL;
1508
0
    int level;
1509
0
    xsltAxis axis = 0;
1510
1511
0
    SKIP_BLANKS;
1512
0
    if ((token == NULL) && (CUR == '@')) {
1513
0
  NEXT;
1514
0
        axis = AXIS_ATTRIBUTE;
1515
0
    }
1516
0
parse_node_test:
1517
0
    if (token == NULL)
1518
0
  token = xsltScanNCName(ctxt);
1519
0
    if (token == NULL) {
1520
0
  if (CUR == '*') {
1521
0
      NEXT;
1522
0
      if (axis == AXIS_ATTRIBUTE) {
1523
0
                PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1524
0
            }
1525
0
            else {
1526
0
                PUSH(XSLT_OP_ALL, NULL, NULL, novar);
1527
0
            }
1528
0
      goto parse_predicate;
1529
0
  } else {
1530
0
      xsltTransformError(NULL, NULL, NULL,
1531
0
        "xsltCompileStepPattern : Name expected\n");
1532
0
      ctxt->error = 1;
1533
0
      goto error;
1534
0
  }
1535
0
    }
1536
1537
1538
0
    SKIP_BLANKS;
1539
0
    if (CUR == '(') {
1540
0
  xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
1541
0
  xmlFree(token);
1542
0
  token = NULL;
1543
0
  if (ctxt->error)
1544
0
      goto error;
1545
0
    } else if (CUR == ':') {
1546
0
  NEXT;
1547
0
  if (CUR != ':') {
1548
0
      xmlChar *prefix = token;
1549
0
      xmlNsPtr ns;
1550
1551
      /*
1552
       * This is a namespace match
1553
       */
1554
0
      token = xsltScanNCName(ctxt);
1555
0
      ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1556
0
      if (ns == NULL) {
1557
0
    xsltTransformError(NULL, NULL, NULL,
1558
0
      "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1559
0
         prefix);
1560
0
    xmlFree(prefix);
1561
0
    prefix=NULL;
1562
0
    ctxt->error = 1;
1563
0
    goto error;
1564
0
      } else {
1565
0
    URL = xmlStrdup(ns->href);
1566
0
      }
1567
0
      xmlFree(prefix);
1568
0
      prefix=NULL;
1569
0
      if (token == NULL) {
1570
0
    if (CUR == '*') {
1571
0
        NEXT;
1572
0
                    if (axis == AXIS_ATTRIBUTE) {
1573
0
                        PUSH(XSLT_OP_ATTR, NULL, URL, novar);
1574
0
      URL = NULL;
1575
0
                    }
1576
0
                    else {
1577
0
                        PUSH(XSLT_OP_NS, URL, NULL, novar);
1578
0
      URL = NULL;
1579
0
                    }
1580
0
    } else {
1581
0
        xsltTransformError(NULL, NULL, NULL,
1582
0
          "xsltCompileStepPattern : Name expected\n");
1583
0
        ctxt->error = 1;
1584
0
        goto error;
1585
0
    }
1586
0
      } else {
1587
0
                if (axis == AXIS_ATTRIBUTE) {
1588
0
                    PUSH(XSLT_OP_ATTR, token, URL, novar);
1589
0
        token = NULL;
1590
0
        URL = NULL;
1591
0
                }
1592
0
                else {
1593
0
                    PUSH(XSLT_OP_ELEM, token, URL, novar);
1594
0
        token = NULL;
1595
0
        URL = NULL;
1596
0
                }
1597
0
      }
1598
0
  } else {
1599
0
      if (axis != 0) {
1600
0
    xsltTransformError(NULL, NULL, NULL,
1601
0
        "xsltCompileStepPattern : NodeTest expected\n");
1602
0
    ctxt->error = 1;
1603
0
    goto error;
1604
0
      }
1605
0
      NEXT;
1606
0
      if (xmlStrEqual(token, (const xmlChar *) "child")) {
1607
0
          axis = AXIS_CHILD;
1608
0
      } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1609
0
          axis = AXIS_ATTRIBUTE;
1610
0
      } else {
1611
0
    xsltTransformError(NULL, NULL, NULL,
1612
0
        "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1613
0
    ctxt->error = 1;
1614
0
    goto error;
1615
0
      }
1616
0
      xmlFree(token);
1617
0
      token = NULL;
1618
0
            SKIP_BLANKS;
1619
0
            token = xsltScanNCName(ctxt);
1620
0
      goto parse_node_test;
1621
0
  }
1622
0
    } else {
1623
0
  URI = xsltGetQNameURI(ctxt->elem, &token);
1624
0
  if (token == NULL) {
1625
0
      ctxt->error = 1;
1626
0
      goto error;
1627
0
  }
1628
0
  if (URI != NULL)
1629
0
      URL = xmlStrdup(URI);
1630
0
        if (axis == AXIS_ATTRIBUTE) {
1631
0
            PUSH(XSLT_OP_ATTR, token, URL, novar);
1632
0
      token = NULL;
1633
0
      URL = NULL;
1634
0
        }
1635
0
        else {
1636
0
            PUSH(XSLT_OP_ELEM, token, URL, novar);
1637
0
      token = NULL;
1638
0
      URL = NULL;
1639
0
        }
1640
0
    }
1641
0
parse_predicate:
1642
0
    SKIP_BLANKS;
1643
0
    level = 0;
1644
0
    while (CUR == '[') {
1645
0
  const xmlChar *q;
1646
1647
0
  level++;
1648
0
  NEXT;
1649
0
  q = CUR_PTR;
1650
0
  while (CUR != 0) {
1651
      /* Skip over nested predicates */
1652
0
      if (CUR == '[')
1653
0
    level++;
1654
0
      else if (CUR == ']') {
1655
0
    level--;
1656
0
    if (level == 0)
1657
0
        break;
1658
0
      } else if (CUR == '"') {
1659
0
    NEXT;
1660
0
    while ((CUR != 0) && (CUR != '"'))
1661
0
        NEXT;
1662
0
      } else if (CUR == '\'') {
1663
0
    NEXT;
1664
0
    while ((CUR != 0) && (CUR != '\''))
1665
0
        NEXT;
1666
0
      }
1667
0
      NEXT;
1668
0
  }
1669
0
  if (CUR == 0) {
1670
0
      xsltTransformError(NULL, NULL, NULL,
1671
0
        "xsltCompileStepPattern : ']' expected\n");
1672
0
      ctxt->error = 1;
1673
0
      return;
1674
0
        }
1675
0
  ret = xmlStrndup(q, CUR_PTR - q);
1676
0
  PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
1677
0
  ret = NULL;
1678
  /* push the predicate lower than local test */
1679
0
  SWAP();
1680
0
  NEXT;
1681
0
  SKIP_BLANKS;
1682
0
    }
1683
0
    return;
1684
0
error:
1685
0
    if (token != NULL)
1686
0
  xmlFree(token);
1687
0
    if (name != NULL)
1688
0
  xmlFree(name);
1689
0
    if (URL != NULL)
1690
0
  xmlFree(URL);
1691
0
    if (ret != NULL)
1692
0
  xmlFree(ret);
1693
0
}
1694
1695
/**
1696
 * xsltCompileRelativePathPattern:
1697
 * @comp:  the compilation context
1698
 * @token:  a posible precompiled name
1699
 * @novar:  flag to prohibit xslt variables
1700
 *
1701
 * Compile the XSLT RelativePathPattern and generates a precompiled
1702
 * form suitable for fast matching.
1703
 *
1704
 * [4] RelativePathPattern ::= StepPattern
1705
 *                           | RelativePathPattern '/' StepPattern
1706
 *                           | RelativePathPattern '//' StepPattern
1707
 */
1708
static void
1709
0
xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1710
0
    xsltCompileStepPattern(ctxt, token, novar);
1711
0
    if (ctxt->error)
1712
0
  goto error;
1713
0
    SKIP_BLANKS;
1714
0
    while ((CUR != 0) && (CUR != '|')) {
1715
0
  if ((CUR == '/') && (NXT(1) == '/')) {
1716
0
      PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1717
0
      NEXT;
1718
0
      NEXT;
1719
0
      SKIP_BLANKS;
1720
0
      xsltCompileStepPattern(ctxt, NULL, novar);
1721
0
  } else if (CUR == '/') {
1722
0
      PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1723
0
      NEXT;
1724
0
      SKIP_BLANKS;
1725
0
      xsltCompileStepPattern(ctxt, NULL, novar);
1726
0
  } else {
1727
0
      ctxt->error = 1;
1728
0
  }
1729
0
  if (ctxt->error)
1730
0
      goto error;
1731
0
  SKIP_BLANKS;
1732
0
    }
1733
0
error:
1734
0
    return;
1735
0
}
1736
1737
/**
1738
 * xsltCompileLocationPathPattern:
1739
 * @ctxt:  the compilation context
1740
 * @novar:  flag to prohibit xslt variables
1741
 *
1742
 * Compile the XSLT LocationPathPattern and generates a precompiled
1743
 * form suitable for fast matching.
1744
 *
1745
 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1746
 *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
1747
 *                           | '//'? RelativePathPattern
1748
 */
1749
static void
1750
0
xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
1751
0
    SKIP_BLANKS;
1752
0
    if ((CUR == '/') && (NXT(1) == '/')) {
1753
  /*
1754
   * since we reverse the query
1755
   * a leading // can be safely ignored
1756
   */
1757
0
  NEXT;
1758
0
  NEXT;
1759
0
  ctxt->comp->priority = 0.5; /* '//' means not 0 priority */
1760
0
  xsltCompileRelativePathPattern(ctxt, NULL, novar);
1761
0
    } else if (CUR == '/') {
1762
  /*
1763
   * We need to find root as the parent
1764
   */
1765
0
  NEXT;
1766
0
  SKIP_BLANKS;
1767
0
  PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
1768
0
  if ((CUR != 0) && (CUR != '|')) {
1769
0
      PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1770
0
      xsltCompileRelativePathPattern(ctxt, NULL, novar);
1771
0
  }
1772
0
    } else if (CUR == '*') {
1773
0
  xsltCompileRelativePathPattern(ctxt, NULL, novar);
1774
0
    } else if (CUR == '@') {
1775
0
  xsltCompileRelativePathPattern(ctxt, NULL, novar);
1776
0
    } else {
1777
0
  xmlChar *name;
1778
0
  name = xsltScanNCName(ctxt);
1779
0
  if (name == NULL) {
1780
0
      xsltTransformError(NULL, NULL, NULL,
1781
0
        "xsltCompileLocationPathPattern : Name expected\n");
1782
0
      ctxt->error = 1;
1783
0
      return;
1784
0
  }
1785
0
  SKIP_BLANKS;
1786
0
  if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
1787
0
      xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
1788
0
      xmlFree(name);
1789
0
      name = NULL;
1790
0
            if (ctxt->error)
1791
0
                return;
1792
0
      if ((CUR == '/') && (NXT(1) == '/')) {
1793
0
    PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1794
0
    NEXT;
1795
0
    NEXT;
1796
0
    SKIP_BLANKS;
1797
0
    xsltCompileRelativePathPattern(ctxt, NULL, novar);
1798
0
      } else if (CUR == '/') {
1799
0
    PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1800
0
    NEXT;
1801
0
    SKIP_BLANKS;
1802
0
    xsltCompileRelativePathPattern(ctxt, NULL, novar);
1803
0
      }
1804
0
      return;
1805
0
  }
1806
0
  xsltCompileRelativePathPattern(ctxt, name, novar);
1807
0
    }
1808
0
error:
1809
0
    return;
1810
0
}
1811
1812
/**
1813
 * xsltCompilePatternInternal:
1814
 * @pattern: an XSLT pattern
1815
 * @doc:  the containing document
1816
 * @node:  the containing element
1817
 * @style:  the stylesheet
1818
 * @runtime:  the transformation context, if done at run-time
1819
 * @novar:  flag to prohibit xslt variables
1820
 *
1821
 * Compile the XSLT pattern and generates a list of precompiled form suitable
1822
 * for fast matching.
1823
 *
1824
 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1825
 *
1826
 * Returns the generated pattern list or NULL in case of failure
1827
 */
1828
1829
static xsltCompMatchPtr
1830
xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc,
1831
             xmlNodePtr node, xsltStylesheetPtr style,
1832
0
       xsltTransformContextPtr runtime, int novar) {
1833
0
    xsltParserContextPtr ctxt = NULL;
1834
0
    xsltCompMatchPtr element, first = NULL, previous = NULL;
1835
0
    int current, start, end, level, j;
1836
1837
0
    if (pattern == NULL) {
1838
0
  xsltTransformError(NULL, NULL, node,
1839
0
       "xsltCompilePattern : NULL pattern\n");
1840
0
  return(NULL);
1841
0
    }
1842
1843
0
    ctxt = xsltNewParserContext(style, runtime);
1844
0
    if (ctxt == NULL)
1845
0
  return(NULL);
1846
0
    ctxt->doc = doc;
1847
0
    ctxt->elem = node;
1848
0
    current = end = 0;
1849
0
    while (pattern[current] != 0) {
1850
0
  start = current;
1851
0
  while (xmlIsBlank_ch(pattern[current]))
1852
0
      current++;
1853
0
  end = current;
1854
0
  level = 0;
1855
0
  while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
1856
0
      if (pattern[end] == '[')
1857
0
    level++;
1858
0
      else if (pattern[end] == ']')
1859
0
    level--;
1860
0
      else if (pattern[end] == '\'') {
1861
0
    end++;
1862
0
    while ((pattern[end] != 0) && (pattern[end] != '\''))
1863
0
        end++;
1864
0
      } else if (pattern[end] == '"') {
1865
0
    end++;
1866
0
    while ((pattern[end] != 0) && (pattern[end] != '"'))
1867
0
        end++;
1868
0
      }
1869
0
      if (pattern[end] == 0)
1870
0
          break;
1871
0
      end++;
1872
0
  }
1873
0
  if (current == end) {
1874
0
      xsltTransformError(NULL, NULL, node,
1875
0
           "xsltCompilePattern : NULL pattern\n");
1876
0
      goto error;
1877
0
  }
1878
0
  element = xsltNewCompMatch();
1879
0
  if (element == NULL) {
1880
0
      goto error;
1881
0
  }
1882
0
  if (first == NULL)
1883
0
      first = element;
1884
0
  else if (previous != NULL)
1885
0
      previous->next = element;
1886
0
  previous = element;
1887
1888
0
  ctxt->comp = element;
1889
0
  ctxt->base = xmlStrndup(&pattern[start], end - start);
1890
0
  if (ctxt->base == NULL)
1891
0
      goto error;
1892
0
  ctxt->cur = &(ctxt->base)[current - start];
1893
0
  element->pattern = ctxt->base;
1894
0
        element->node = node;
1895
0
  element->nsList = xmlGetNsList(doc, node);
1896
0
  j = 0;
1897
0
  if (element->nsList != NULL) {
1898
0
      while (element->nsList[j] != NULL)
1899
0
    j++;
1900
0
  }
1901
0
  element->nsNr = j;
1902
1903
1904
#ifdef WITH_XSLT_DEBUG_PATTERN
1905
  xsltGenericDebug(xsltGenericDebugContext,
1906
       "xsltCompilePattern : parsing '%s'\n",
1907
       element->pattern);
1908
#endif
1909
  /*
1910
   Preset default priority to be zero.
1911
   This may be changed by xsltCompileLocationPathPattern.
1912
   */
1913
0
  element->priority = 0;
1914
0
  xsltCompileLocationPathPattern(ctxt, novar);
1915
0
  if (ctxt->error) {
1916
0
      xsltTransformError(NULL, style, node,
1917
0
           "xsltCompilePattern : failed to compile '%s'\n",
1918
0
           element->pattern);
1919
0
      if (style != NULL) style->errors++;
1920
0
      goto error;
1921
0
  }
1922
1923
  /*
1924
   * Reverse for faster interpretation.
1925
   */
1926
0
  xsltReverseCompMatch(ctxt, element);
1927
1928
  /*
1929
   * Set-up the priority
1930
   */
1931
0
  if (element->priority == 0) { /* if not yet determined */
1932
0
      if (((element->steps[0].op == XSLT_OP_ELEM) ||
1933
0
     (element->steps[0].op == XSLT_OP_ATTR) ||
1934
0
     (element->steps[0].op == XSLT_OP_PI)) &&
1935
0
    (element->steps[0].value != NULL) &&
1936
0
    (element->steps[1].op == XSLT_OP_END)) {
1937
0
    ; /* previously preset */
1938
0
      } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1939
0
           (element->steps[0].value2 != NULL) &&
1940
0
           (element->steps[1].op == XSLT_OP_END)) {
1941
0
      element->priority = -0.25;
1942
0
      } else if ((element->steps[0].op == XSLT_OP_NS) &&
1943
0
           (element->steps[0].value != NULL) &&
1944
0
           (element->steps[1].op == XSLT_OP_END)) {
1945
0
      element->priority = -0.25;
1946
0
      } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1947
0
           (element->steps[0].value == NULL) &&
1948
0
           (element->steps[0].value2 == NULL) &&
1949
0
           (element->steps[1].op == XSLT_OP_END)) {
1950
0
      element->priority = -0.5;
1951
0
      } else if (((element->steps[0].op == XSLT_OP_PI) ||
1952
0
           (element->steps[0].op == XSLT_OP_TEXT) ||
1953
0
           (element->steps[0].op == XSLT_OP_ALL) ||
1954
0
           (element->steps[0].op == XSLT_OP_NODE) ||
1955
0
           (element->steps[0].op == XSLT_OP_COMMENT)) &&
1956
0
           (element->steps[1].op == XSLT_OP_END)) {
1957
0
      element->priority = -0.5;
1958
0
      } else {
1959
0
    element->priority = 0.5;
1960
0
      }
1961
0
  }
1962
#ifdef WITH_XSLT_DEBUG_PATTERN
1963
  xsltGenericDebug(xsltGenericDebugContext,
1964
         "xsltCompilePattern : parsed %s, default priority %f\n",
1965
       element->pattern, element->priority);
1966
#endif
1967
0
  if (pattern[end] == '|')
1968
0
      end++;
1969
0
  current = end;
1970
0
    }
1971
0
    if (end == 0) {
1972
0
  xsltTransformError(NULL, style, node,
1973
0
       "xsltCompilePattern : NULL pattern\n");
1974
0
  if (style != NULL) style->errors++;
1975
0
  goto error;
1976
0
    }
1977
1978
0
    xsltFreeParserContext(ctxt);
1979
0
    return(first);
1980
1981
0
error:
1982
0
    if (ctxt != NULL)
1983
0
  xsltFreeParserContext(ctxt);
1984
0
    if (first != NULL)
1985
0
  xsltFreeCompMatchList(first);
1986
0
    return(NULL);
1987
0
}
1988
1989
/**
1990
 * xsltCompilePattern:
1991
 * @pattern: an XSLT pattern
1992
 * @doc:  the containing document
1993
 * @node:  the containing element
1994
 * @style:  the stylesheet
1995
 * @runtime:  the transformation context, if done at run-time
1996
 *
1997
 * Compile the XSLT pattern and generates a list of precompiled form suitable
1998
 * for fast matching.
1999
 *
2000
 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
2001
 *
2002
 * Returns the generated pattern list or NULL in case of failure
2003
 */
2004
2005
xsltCompMatchPtr
2006
xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
2007
             xmlNodePtr node, xsltStylesheetPtr style,
2008
0
       xsltTransformContextPtr runtime) {
2009
0
    return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0));
2010
0
}
2011
2012
/************************************************************************
2013
 *                  *
2014
 *      Module interfaces       *
2015
 *                  *
2016
 ************************************************************************/
2017
2018
/**
2019
 * xsltAddTemplate:
2020
 * @style: an XSLT stylesheet
2021
 * @cur: an XSLT template
2022
 * @mode:  the mode name or NULL
2023
 * @modeURI:  the mode URI or NULL
2024
 *
2025
 * Register the XSLT pattern associated to @cur
2026
 *
2027
 * Returns -1 in case of error, 0 otherwise
2028
 */
2029
int
2030
xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
2031
0
          const xmlChar *mode, const xmlChar *modeURI) {
2032
0
    xsltCompMatchPtr pat, list, next;
2033
    /*
2034
     * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2035
     *  avoids gcc 'type-punned pointer' warning.
2036
     */
2037
0
    xsltCompMatchPtr *top = NULL;
2038
0
    const xmlChar *name = NULL;
2039
0
    float priority;              /* the priority */
2040
2041
0
    if ((style == NULL) || (cur == NULL))
2042
0
  return(-1);
2043
2044
0
    if (cur->next != NULL)
2045
0
        cur->position = cur->next->position + 1;
2046
2047
    /* Register named template */
2048
0
    if (cur->name != NULL) {
2049
0
        if (style->namedTemplates == NULL) {
2050
0
            style->namedTemplates = xmlHashCreate(10);
2051
0
            if (style->namedTemplates == NULL)
2052
0
                return(-1);
2053
0
        }
2054
0
        else {
2055
0
            void *dup = xmlHashLookup2(style->namedTemplates, cur->name,
2056
0
                                       cur->nameURI);
2057
0
            if (dup != NULL) {
2058
0
                xsltTransformError(NULL, style, cur->elem,
2059
0
                                   "xsl:template: error duplicate name '%s'\n",
2060
0
                                   cur->name);
2061
0
                style->errors++;
2062
0
                return(-1);
2063
0
            }
2064
0
        }
2065
2066
0
        xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur);
2067
0
    }
2068
2069
0
    if (cur->match == NULL) {
2070
0
            if (cur->name == NULL) {
2071
0
                xsltTransformError(NULL, style, cur->elem,
2072
0
                    "xsl:template: need to specify match or name attribute\n");
2073
0
                style->errors++;
2074
0
                return(-1);
2075
0
            }
2076
0
  return(0);
2077
0
    }
2078
2079
0
    priority = cur->priority;
2080
0
    pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem,
2081
0
        style, NULL, 1);
2082
0
    if (pat == NULL)
2083
0
  return(-1);
2084
0
    while (pat) {
2085
0
        int success = 0;
2086
2087
0
  next = pat->next;
2088
0
  pat->next = NULL;
2089
0
  name = NULL;
2090
2091
0
  pat->template = cur;
2092
0
  if (mode != NULL)
2093
0
      pat->mode = xmlDictLookup(style->dict, mode, -1);
2094
0
  if (modeURI != NULL)
2095
0
      pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
2096
0
  if (priority != XSLT_PAT_NO_PRIORITY)
2097
0
      pat->priority = priority;
2098
2099
  /*
2100
   * insert it in the hash table list corresponding to its lookup name
2101
   */
2102
0
  switch (pat->steps[0].op) {
2103
0
        case XSLT_OP_ATTR:
2104
0
      if (pat->steps[0].value != NULL)
2105
0
    name = pat->steps[0].value;
2106
0
      else
2107
0
    top = &(style->attrMatch);
2108
0
      break;
2109
0
        case XSLT_OP_PARENT:
2110
0
        case XSLT_OP_ANCESTOR:
2111
0
      top = &(style->elemMatch);
2112
0
      break;
2113
0
        case XSLT_OP_ROOT:
2114
0
      top = &(style->rootMatch);
2115
0
      break;
2116
0
        case XSLT_OP_KEY:
2117
0
      top = &(style->keyMatch);
2118
0
      break;
2119
0
        case XSLT_OP_ID:
2120
      /* TODO optimize ID !!! */
2121
0
        case XSLT_OP_NS:
2122
0
        case XSLT_OP_ALL:
2123
0
      top = &(style->elemMatch);
2124
0
      break;
2125
0
        case XSLT_OP_END:
2126
0
  case XSLT_OP_PREDICATE:
2127
0
      xsltTransformError(NULL, style, NULL,
2128
0
           "xsltAddTemplate: invalid compiled pattern\n");
2129
0
      xsltFreeCompMatch(pat);
2130
0
      return(-1);
2131
      /*
2132
       * TODO: some flags at the top level about type based patterns
2133
       *       would be faster than inclusion in the hash table.
2134
       */
2135
0
  case XSLT_OP_PI:
2136
0
      if (pat->steps[0].value != NULL)
2137
0
    name = pat->steps[0].value;
2138
0
      else
2139
0
    top = &(style->piMatch);
2140
0
      break;
2141
0
  case XSLT_OP_COMMENT:
2142
0
      top = &(style->commentMatch);
2143
0
      break;
2144
0
  case XSLT_OP_TEXT:
2145
0
      top = &(style->textMatch);
2146
0
      break;
2147
0
        case XSLT_OP_ELEM:
2148
0
  case XSLT_OP_NODE:
2149
0
      if (pat->steps[0].value != NULL)
2150
0
    name = pat->steps[0].value;
2151
0
      else
2152
0
    top = &(style->elemMatch);
2153
0
      break;
2154
0
  }
2155
0
  if (name != NULL) {
2156
0
      if (style->templatesHash == NULL) {
2157
0
    style->templatesHash = xmlHashCreate(1024);
2158
0
    success = (style->templatesHash != NULL) &&
2159
0
                          (xmlHashAddEntry3(style->templatesHash, name, mode,
2160
0
                                            modeURI, pat) >= 0);
2161
0
      } else {
2162
0
    list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
2163
0
               name, mode, modeURI);
2164
0
    if (list == NULL) {
2165
0
        success = (xmlHashAddEntry3(style->templatesHash, name,
2166
0
                        mode, modeURI, pat) >= 0);
2167
0
    } else {
2168
        /*
2169
         * Note '<=' since one must choose among the matching
2170
         * template rules that are left, the one that occurs
2171
         * last in the stylesheet
2172
         */
2173
0
        if (list->priority <= pat->priority) {
2174
0
      pat->next = list;
2175
0
      xmlHashUpdateEntry3(style->templatesHash, name,
2176
0
              mode, modeURI, pat, NULL);
2177
0
        } else {
2178
0
      while (list->next != NULL) {
2179
0
          if (list->next->priority <= pat->priority)
2180
0
        break;
2181
0
          list = list->next;
2182
0
      }
2183
0
      pat->next = list->next;
2184
0
      list->next = pat;
2185
0
        }
2186
0
                    success = 1;
2187
0
    }
2188
0
      }
2189
0
  } else if (top != NULL) {
2190
0
      list = *top;
2191
0
      if (list == NULL) {
2192
0
    *top = pat;
2193
0
    pat->next = NULL;
2194
0
      } else if (list->priority <= pat->priority) {
2195
0
    pat->next = list;
2196
0
    *top = pat;
2197
0
      } else {
2198
0
    while (list->next != NULL) {
2199
0
        if (list->next->priority <= pat->priority)
2200
0
      break;
2201
0
        list = list->next;
2202
0
    }
2203
0
    pat->next = list->next;
2204
0
    list->next = pat;
2205
0
      }
2206
0
            success = 1;
2207
0
  }
2208
0
        if (success == 0) {
2209
0
      xsltTransformError(NULL, style, NULL,
2210
0
           "xsltAddTemplate: invalid compiled pattern\n");
2211
0
      xsltFreeCompMatch(pat);
2212
0
            xsltFreeCompMatchList(next);
2213
0
      return(-1);
2214
0
  }
2215
#ifdef WITH_XSLT_DEBUG_PATTERN
2216
  if (mode)
2217
      xsltGenericDebug(xsltGenericDebugContext,
2218
       "added pattern : '%s' mode '%s' priority %f\n",
2219
           pat->pattern, pat->mode, pat->priority);
2220
  else
2221
      xsltGenericDebug(xsltGenericDebugContext,
2222
       "added pattern : '%s' priority %f\n",
2223
           pat->pattern, pat->priority);
2224
#endif
2225
2226
0
  pat = next;
2227
0
    }
2228
0
    return(0);
2229
0
}
2230
2231
static int
2232
xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
2233
0
{
2234
0
    if ((ctxt == NULL) || (contextNode == NULL)) {
2235
0
  xsltTransformError(ctxt, NULL, ctxt->inst,
2236
0
      "Internal error in xsltComputeAllKeys(): "
2237
0
      "Bad arguments.\n");
2238
0
  return(-1);
2239
0
    }
2240
2241
0
    if (ctxt->document == NULL) {
2242
  /*
2243
  * The document info will only be NULL if we have a RTF.
2244
  */
2245
0
  if (contextNode->doc->_private != NULL)
2246
0
      goto doc_info_mismatch;
2247
  /*
2248
  * On-demand creation of the document info (needed for keys).
2249
  */
2250
0
  ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
2251
0
  if (ctxt->document == NULL)
2252
0
      return(-1);
2253
0
    }
2254
0
    return xsltInitAllDocKeys(ctxt);
2255
2256
0
doc_info_mismatch:
2257
0
    xsltTransformError(ctxt, NULL, ctxt->inst,
2258
0
  "Internal error in xsltComputeAllKeys(): "
2259
0
  "The context's document info doesn't match the "
2260
0
  "document info of the current result tree.\n");
2261
0
    ctxt->state = XSLT_STATE_STOPPED;
2262
0
    return(-1);
2263
0
}
2264
2265
/**
2266
 * xsltGetTemplate:
2267
 * @ctxt:  a XSLT process context
2268
 * @node:  the node being processed
2269
 * @style:  the current style
2270
 *
2271
 * Finds the template applying to this node, if @style is non-NULL
2272
 * it means one needs to look for the next imported template in scope.
2273
 *
2274
 * Returns the xsltTemplatePtr or NULL if not found
2275
 */
2276
xsltTemplatePtr
2277
xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2278
          xsltStylesheetPtr style)
2279
0
{
2280
0
    xsltStylesheetPtr curstyle;
2281
0
    xsltTemplatePtr ret = NULL;
2282
0
    const xmlChar *name = NULL;
2283
0
    xsltCompMatchPtr list = NULL;
2284
0
    float priority;
2285
2286
0
    if ((ctxt == NULL) || (node == NULL))
2287
0
  return(NULL);
2288
2289
0
    if (style == NULL) {
2290
0
  curstyle = ctxt->style;
2291
0
    } else {
2292
0
  curstyle = xsltNextImport(style);
2293
0
    }
2294
2295
0
    while ((curstyle != NULL) && (curstyle != style)) {
2296
0
  priority = XSLT_PAT_NO_PRIORITY;
2297
  /* TODO : handle IDs/keys here ! */
2298
0
  if (curstyle->templatesHash != NULL) {
2299
      /*
2300
       * Use the top name as selector
2301
       */
2302
0
      switch (node->type) {
2303
0
    case XML_ELEMENT_NODE:
2304
0
        if (node->name[0] == ' ')
2305
0
      break;
2306
                    /* Intentional fall-through */
2307
0
    case XML_ATTRIBUTE_NODE:
2308
0
    case XML_PI_NODE:
2309
0
        name = node->name;
2310
0
        break;
2311
0
    case XML_DOCUMENT_NODE:
2312
0
    case XML_HTML_DOCUMENT_NODE:
2313
0
    case XML_TEXT_NODE:
2314
0
    case XML_CDATA_SECTION_NODE:
2315
0
    case XML_COMMENT_NODE:
2316
0
    case XML_ENTITY_REF_NODE:
2317
0
    case XML_ENTITY_NODE:
2318
0
    case XML_DOCUMENT_TYPE_NODE:
2319
0
    case XML_DOCUMENT_FRAG_NODE:
2320
0
    case XML_NOTATION_NODE:
2321
0
    case XML_DTD_NODE:
2322
0
    case XML_ELEMENT_DECL:
2323
0
    case XML_ATTRIBUTE_DECL:
2324
0
    case XML_ENTITY_DECL:
2325
0
    case XML_NAMESPACE_DECL:
2326
0
    case XML_XINCLUDE_START:
2327
0
    case XML_XINCLUDE_END:
2328
0
        break;
2329
0
    default:
2330
0
        return(NULL);
2331
2332
0
      }
2333
0
  }
2334
0
  if (name != NULL) {
2335
      /*
2336
       * find the list of applicable expressions based on the name
2337
       */
2338
0
      list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
2339
0
               name, ctxt->mode, ctxt->modeURI);
2340
0
  } else
2341
0
      list = NULL;
2342
0
  while (list != NULL) {
2343
0
      if (xsltTestCompMatch(ctxt, list, node,
2344
0
                ctxt->mode, ctxt->modeURI) == 1) {
2345
0
    ret = list->template;
2346
0
    priority = list->priority;
2347
0
    break;
2348
0
      }
2349
0
      list = list->next;
2350
0
  }
2351
0
  list = NULL;
2352
2353
  /*
2354
   * find alternate generic matches
2355
   */
2356
0
  switch (node->type) {
2357
0
      case XML_ELEMENT_NODE:
2358
0
    if (node->name[0] == ' ')
2359
0
        list = curstyle->rootMatch;
2360
0
    else
2361
0
        list = curstyle->elemMatch;
2362
0
    break;
2363
0
      case XML_ATTRIBUTE_NODE: {
2364
0
    list = curstyle->attrMatch;
2365
0
    break;
2366
0
      }
2367
0
      case XML_PI_NODE:
2368
0
    list = curstyle->piMatch;
2369
0
    break;
2370
0
      case XML_DOCUMENT_NODE:
2371
0
      case XML_HTML_DOCUMENT_NODE: {
2372
0
    list = curstyle->rootMatch;
2373
0
    break;
2374
0
      }
2375
0
      case XML_TEXT_NODE:
2376
0
      case XML_CDATA_SECTION_NODE:
2377
0
    list = curstyle->textMatch;
2378
0
    break;
2379
0
      case XML_COMMENT_NODE:
2380
0
    list = curstyle->commentMatch;
2381
0
    break;
2382
0
      case XML_ENTITY_REF_NODE:
2383
0
      case XML_ENTITY_NODE:
2384
0
      case XML_DOCUMENT_TYPE_NODE:
2385
0
      case XML_DOCUMENT_FRAG_NODE:
2386
0
      case XML_NOTATION_NODE:
2387
0
      case XML_DTD_NODE:
2388
0
      case XML_ELEMENT_DECL:
2389
0
      case XML_ATTRIBUTE_DECL:
2390
0
      case XML_ENTITY_DECL:
2391
0
      case XML_NAMESPACE_DECL:
2392
0
      case XML_XINCLUDE_START:
2393
0
      case XML_XINCLUDE_END:
2394
0
    break;
2395
0
      default:
2396
0
    break;
2397
0
  }
2398
0
  while ((list != NULL) &&
2399
0
         ((ret == NULL) ||
2400
0
                (list->priority > priority) ||
2401
0
                ((list->priority == priority) &&
2402
0
                 (list->template->position > ret->position)))) {
2403
0
      if (xsltTestCompMatch(ctxt, list, node,
2404
0
                ctxt->mode, ctxt->modeURI) == 1) {
2405
0
    ret = list->template;
2406
0
    priority = list->priority;
2407
0
    break;
2408
0
      }
2409
0
      list = list->next;
2410
0
  }
2411
  /*
2412
   * Some of the tests for elements can also apply to documents
2413
   */
2414
0
  if ((node->type == XML_DOCUMENT_NODE) ||
2415
0
      (node->type == XML_HTML_DOCUMENT_NODE) ||
2416
0
      (node->type == XML_TEXT_NODE)) {
2417
0
      list = curstyle->elemMatch;
2418
0
      while ((list != NULL) &&
2419
0
                   ((ret == NULL) ||
2420
0
                    (list->priority > priority) ||
2421
0
                    ((list->priority == priority) &&
2422
0
                     (list->template->position > ret->position)))) {
2423
0
    if (xsltTestCompMatch(ctxt, list, node,
2424
0
              ctxt->mode, ctxt->modeURI) == 1) {
2425
0
        ret = list->template;
2426
0
        priority = list->priority;
2427
0
        break;
2428
0
    }
2429
0
    list = list->next;
2430
0
      }
2431
0
  } else if ((node->type == XML_PI_NODE) ||
2432
0
       (node->type == XML_COMMENT_NODE)) {
2433
0
      list = curstyle->elemMatch;
2434
0
      while ((list != NULL) &&
2435
0
                   ((ret == NULL) ||
2436
0
                    (list->priority > priority) ||
2437
0
                    ((list->priority == priority) &&
2438
0
                     (list->template->position > ret->position)))) {
2439
0
    if (xsltTestCompMatch(ctxt, list, node,
2440
0
              ctxt->mode, ctxt->modeURI) == 1) {
2441
0
        ret = list->template;
2442
0
        priority = list->priority;
2443
0
        break;
2444
0
    }
2445
0
    list = list->next;
2446
0
      }
2447
0
  }
2448
2449
0
keyed_match:
2450
0
        if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY) {
2451
0
      list = curstyle->keyMatch;
2452
0
      while ((list != NULL) &&
2453
0
                   ((ret == NULL) ||
2454
0
                    (list->priority > priority) ||
2455
0
                    ((list->priority == priority) &&
2456
0
                     (list->template->position > ret->position)))) {
2457
0
    if (xsltTestCompMatch(ctxt, list, node,
2458
0
              ctxt->mode, ctxt->modeURI) == 1) {
2459
0
        ret = list->template;
2460
0
        priority = list->priority;
2461
0
        break;
2462
0
    }
2463
0
    list = list->next;
2464
0
      }
2465
0
  }
2466
0
  else if (ctxt->hasTemplKeyPatterns &&
2467
0
      ((ctxt->document == NULL) ||
2468
0
       (ctxt->document->nbKeysComputed < ctxt->nbKeys)))
2469
0
  {
2470
      /*
2471
      * Compute all remaining keys for this document.
2472
      *
2473
      * REVISIT TODO: I think this could be further optimized.
2474
      */
2475
0
      if (xsltComputeAllKeys(ctxt, node) == -1)
2476
0
    goto error;
2477
2478
0
            if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY)
2479
0
    goto keyed_match;
2480
0
  }
2481
0
  if (ret != NULL)
2482
0
      return(ret);
2483
2484
  /*
2485
   * Cycle on next curstylesheet import.
2486
   */
2487
0
  curstyle = xsltNextImport(curstyle);
2488
0
    }
2489
2490
0
error:
2491
0
    return(NULL);
2492
0
}
2493
2494
/**
2495
 * xsltCleanupTemplates:
2496
 * @style: an XSLT stylesheet
2497
 *
2498
 * Cleanup the state of the templates used by the stylesheet and
2499
 * the ones it imports.
2500
 */
2501
void
2502
0
xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
2503
0
}
2504
2505
/**
2506
 * xsltFreeTemplateHashes:
2507
 * @style: an XSLT stylesheet
2508
 *
2509
 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2510
 */
2511
void
2512
0
xsltFreeTemplateHashes(xsltStylesheetPtr style) {
2513
0
    if (style->templatesHash != NULL)
2514
0
  xmlHashFree(style->templatesHash, xsltFreeCompMatchListEntry);
2515
0
    if (style->rootMatch != NULL)
2516
0
        xsltFreeCompMatchList(style->rootMatch);
2517
0
    if (style->keyMatch != NULL)
2518
0
        xsltFreeCompMatchList(style->keyMatch);
2519
0
    if (style->elemMatch != NULL)
2520
0
        xsltFreeCompMatchList(style->elemMatch);
2521
0
    if (style->attrMatch != NULL)
2522
0
        xsltFreeCompMatchList(style->attrMatch);
2523
0
    if (style->parentMatch != NULL)
2524
0
        xsltFreeCompMatchList(style->parentMatch);
2525
0
    if (style->textMatch != NULL)
2526
0
        xsltFreeCompMatchList(style->textMatch);
2527
0
    if (style->piMatch != NULL)
2528
0
        xsltFreeCompMatchList(style->piMatch);
2529
0
    if (style->commentMatch != NULL)
2530
0
        xsltFreeCompMatchList(style->commentMatch);
2531
0
    if (style->namedTemplates != NULL)
2532
0
        xmlHashFree(style->namedTemplates, NULL);
2533
0
}
2534