Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/xslt/libxslt/pattern.c
4393 views
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
* [email protected]
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
xsltNewCompMatch(void) {
146
xsltCompMatchPtr cur;
147
148
cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
149
if (cur == NULL) {
150
xsltTransformError(NULL, NULL, NULL,
151
"xsltNewCompMatch : out of memory error\n");
152
return(NULL);
153
}
154
memset(cur, 0, sizeof(xsltCompMatch));
155
cur->maxStep = 10;
156
cur->nbStep = 0;
157
cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) *
158
cur->maxStep);
159
if (cur->steps == NULL) {
160
xsltTransformError(NULL, NULL, NULL,
161
"xsltNewCompMatch : out of memory error\n");
162
xmlFree(cur);
163
return(NULL);
164
}
165
cur->nsNr = 0;
166
cur->nsList = NULL;
167
cur->direct = 0;
168
return(cur);
169
}
170
171
/**
172
* xsltFreeCompMatch:
173
* @comp: an XSLT comp
174
*
175
* Free up the memory allocated by @comp
176
*/
177
static void
178
xsltFreeCompMatch(xsltCompMatchPtr comp) {
179
xsltStepOpPtr op;
180
int i;
181
182
if (comp == NULL)
183
return;
184
if (comp->pattern != NULL)
185
xmlFree((xmlChar *)comp->pattern);
186
if (comp->nsList != NULL)
187
xmlFree(comp->nsList);
188
for (i = 0;i < comp->nbStep;i++) {
189
op = &comp->steps[i];
190
if (op->value != NULL)
191
xmlFree(op->value);
192
if (op->value2 != NULL)
193
xmlFree(op->value2);
194
if (op->value3 != NULL)
195
xmlFree(op->value3);
196
if (op->comp != NULL)
197
xmlXPathFreeCompExpr(op->comp);
198
}
199
xmlFree(comp->steps);
200
memset(comp, -1, sizeof(xsltCompMatch));
201
xmlFree(comp);
202
}
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
xsltFreeCompMatchList(xsltCompMatchPtr comp) {
212
xsltCompMatchPtr cur;
213
214
while (comp != NULL) {
215
cur = comp;
216
comp = comp->next;
217
xsltFreeCompMatch(cur);
218
}
219
}
220
221
static void
222
xsltFreeCompMatchListEntry(void *payload,
223
const xmlChar *name ATTRIBUTE_UNUSED) {
224
xsltFreeCompMatchList((xsltCompMatchPtr) payload);
225
}
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
void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
238
xsltCompMatchPtr comp = payload;
239
xsltStylesheetPtr style = data;
240
int ix;
241
242
for (ix = 0; ix < comp->nbStep; ix++) {
243
comp->steps[ix].previousExtra += style->extrasNr;
244
comp->steps[ix].indexExtra += style->extrasNr;
245
comp->steps[ix].lenExtra += style->extrasNr;
246
}
247
}
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
xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
260
xsltParserContextPtr cur;
261
262
cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
263
if (cur == NULL) {
264
xsltTransformError(NULL, NULL, NULL,
265
"xsltNewParserContext : malloc failed\n");
266
return(NULL);
267
}
268
memset(cur, 0, sizeof(xsltParserContext));
269
cur->style = style;
270
cur->ctxt = ctxt;
271
return(cur);
272
}
273
274
/**
275
* xsltFreeParserContext:
276
* @ctxt: an XSLT parser context
277
*
278
* Free up the memory allocated by @ctxt
279
*/
280
static void
281
xsltFreeParserContext(xsltParserContextPtr ctxt) {
282
if (ctxt == NULL)
283
return;
284
memset(ctxt, -1, sizeof(xsltParserContext));
285
xmlFree(ctxt);
286
}
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
{
304
if (comp->nbStep >= comp->maxStep) {
305
xsltStepOpPtr tmp;
306
307
tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
308
sizeof(xsltStepOp));
309
if (tmp == NULL) {
310
xsltGenericError(xsltGenericErrorContext,
311
"xsltCompMatchAdd: memory re-allocation failure.\n");
312
if (ctxt->style != NULL)
313
ctxt->style->errors++;
314
return (-1);
315
}
316
comp->maxStep *= 2;
317
comp->steps = tmp;
318
}
319
comp->steps[comp->nbStep].op = op;
320
comp->steps[comp->nbStep].value = value;
321
comp->steps[comp->nbStep].value2 = value2;
322
comp->steps[comp->nbStep].value3 = NULL;
323
comp->steps[comp->nbStep].comp = NULL;
324
if (ctxt->ctxt != NULL) {
325
comp->steps[comp->nbStep].previousExtra =
326
xsltAllocateExtraCtxt(ctxt->ctxt);
327
comp->steps[comp->nbStep].indexExtra =
328
xsltAllocateExtraCtxt(ctxt->ctxt);
329
comp->steps[comp->nbStep].lenExtra =
330
xsltAllocateExtraCtxt(ctxt->ctxt);
331
} else {
332
comp->steps[comp->nbStep].previousExtra =
333
xsltAllocateExtra(ctxt->style);
334
comp->steps[comp->nbStep].indexExtra =
335
xsltAllocateExtra(ctxt->style);
336
comp->steps[comp->nbStep].lenExtra =
337
xsltAllocateExtra(ctxt->style);
338
}
339
if (op == XSLT_OP_PREDICATE) {
340
int flags = 0;
341
342
#ifdef XML_XPATH_NOVAR
343
if (novar != 0)
344
flags = XML_XPATH_NOVAR;
345
#endif
346
comp->steps[comp->nbStep].comp = xsltXPathCompileFlags(ctxt->style,
347
value, flags);
348
if (comp->steps[comp->nbStep].comp == NULL) {
349
xsltTransformError(NULL, ctxt->style, ctxt->elem,
350
"Failed to compile predicate\n");
351
if (ctxt->style != NULL)
352
ctxt->style->errors++;
353
}
354
}
355
comp->nbStep++;
356
return (0);
357
}
358
359
/**
360
* xsltSwapTopCompMatch:
361
* @comp: the compiled match expression
362
*
363
* reverse the two top steps.
364
*/
365
static void
366
xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
367
int i;
368
int j = comp->nbStep - 1;
369
370
if (j > 0) {
371
register xmlChar *tmp;
372
register xsltOp op;
373
register xmlXPathCompExprPtr expr;
374
register int t;
375
i = j - 1;
376
tmp = comp->steps[i].value;
377
comp->steps[i].value = comp->steps[j].value;
378
comp->steps[j].value = tmp;
379
tmp = comp->steps[i].value2;
380
comp->steps[i].value2 = comp->steps[j].value2;
381
comp->steps[j].value2 = tmp;
382
tmp = comp->steps[i].value3;
383
comp->steps[i].value3 = comp->steps[j].value3;
384
comp->steps[j].value3 = tmp;
385
op = comp->steps[i].op;
386
comp->steps[i].op = comp->steps[j].op;
387
comp->steps[j].op = op;
388
expr = comp->steps[i].comp;
389
comp->steps[i].comp = comp->steps[j].comp;
390
comp->steps[j].comp = expr;
391
t = comp->steps[i].previousExtra;
392
comp->steps[i].previousExtra = comp->steps[j].previousExtra;
393
comp->steps[j].previousExtra = t;
394
t = comp->steps[i].indexExtra;
395
comp->steps[i].indexExtra = comp->steps[j].indexExtra;
396
comp->steps[j].indexExtra = t;
397
t = comp->steps[i].lenExtra;
398
comp->steps[i].lenExtra = comp->steps[j].lenExtra;
399
comp->steps[j].lenExtra = t;
400
}
401
}
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
xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) {
412
int i = 0;
413
int j = comp->nbStep - 1;
414
415
while (j > i) {
416
register xmlChar *tmp;
417
register xsltOp op;
418
register xmlXPathCompExprPtr expr;
419
register int t;
420
421
tmp = comp->steps[i].value;
422
comp->steps[i].value = comp->steps[j].value;
423
comp->steps[j].value = tmp;
424
tmp = comp->steps[i].value2;
425
comp->steps[i].value2 = comp->steps[j].value2;
426
comp->steps[j].value2 = tmp;
427
tmp = comp->steps[i].value3;
428
comp->steps[i].value3 = comp->steps[j].value3;
429
comp->steps[j].value3 = tmp;
430
op = comp->steps[i].op;
431
comp->steps[i].op = comp->steps[j].op;
432
comp->steps[j].op = op;
433
expr = comp->steps[i].comp;
434
comp->steps[i].comp = comp->steps[j].comp;
435
comp->steps[j].comp = expr;
436
t = comp->steps[i].previousExtra;
437
comp->steps[i].previousExtra = comp->steps[j].previousExtra;
438
comp->steps[j].previousExtra = t;
439
t = comp->steps[i].indexExtra;
440
comp->steps[i].indexExtra = comp->steps[j].indexExtra;
441
comp->steps[j].indexExtra = t;
442
t = comp->steps[i].lenExtra;
443
comp->steps[i].lenExtra = comp->steps[j].lenExtra;
444
comp->steps[j].lenExtra = t;
445
j--;
446
i++;
447
}
448
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
for (i = 0;i < comp->nbStep - 1;i++) {
455
if ((comp->steps[i].op == XSLT_OP_PREDICATE) &&
456
(comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
457
458
comp->direct = 1;
459
if (comp->pattern[0] != '/') {
460
xmlChar *query;
461
462
query = xmlStrdup((const xmlChar *)"//");
463
query = xmlStrcat(query, comp->pattern);
464
465
xmlFree((xmlChar *) comp->pattern);
466
comp->pattern = query;
467
}
468
break;
469
}
470
}
471
}
472
473
/************************************************************************
474
* *
475
* The interpreter for the precompiled patterns *
476
* *
477
************************************************************************/
478
479
static int
480
xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
481
int step, xmlNodePtr node) {
482
if (states->maxstates <= states->nbstates) {
483
xsltStepState *tmp;
484
int newMax = states->maxstates == 0 ? 4 : 2 * states->maxstates;
485
486
tmp = (xsltStepStatePtr) xmlRealloc(states->states,
487
newMax * sizeof(xsltStepState));
488
if (tmp == NULL) {
489
xsltGenericError(xsltGenericErrorContext,
490
"xsltPatPushState: memory re-allocation failure.\n");
491
ctxt->state = XSLT_STATE_STOPPED;
492
return(-1);
493
}
494
states->states = tmp;
495
states->maxstates = newMax;
496
}
497
states->states[states->nbstates].step = step;
498
states->states[states->nbstates++].node = node;
499
#if 0
500
fprintf(stderr, "Push: %d, %s\n", step, node->name);
501
#endif
502
return(0);
503
}
504
505
static void
506
xmlXPathFreeObjectWrapper(void *obj) {
507
xmlXPathFreeObject((xmlXPathObjectPtr) obj);
508
}
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
xmlNodePtr node, xmlNsPtr *nsList, int nsNr) {
526
xsltStepOpPtr sel = NULL;
527
xmlDocPtr prevdoc;
528
xmlDocPtr doc;
529
xmlXPathObjectPtr list;
530
int ix, j;
531
int nocache = 0;
532
int isRVT;
533
534
doc = node->doc;
535
if (XSLT_IS_RES_TREE_FRAG(doc))
536
isRVT = 1;
537
else
538
isRVT = 0;
539
sel = &comp->steps[0]; /* store extra in first step arbitrarily */
540
541
prevdoc = (xmlDocPtr)
542
XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
543
ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
544
list = (xmlXPathObjectPtr)
545
XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
546
547
if ((list == NULL) || (prevdoc != doc)) {
548
xmlXPathObjectPtr newlist;
549
xmlNodePtr parent = node->parent;
550
xmlDocPtr olddoc;
551
xmlNodePtr oldnode;
552
int oldNsNr, oldContextSize, oldProximityPosition;
553
xmlNsPtr *oldNamespaces;
554
555
oldnode = ctxt->xpathCtxt->node;
556
olddoc = ctxt->xpathCtxt->doc;
557
oldNsNr = ctxt->xpathCtxt->nsNr;
558
oldNamespaces = ctxt->xpathCtxt->namespaces;
559
oldContextSize = ctxt->xpathCtxt->contextSize;
560
oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
561
ctxt->xpathCtxt->node = node;
562
ctxt->xpathCtxt->doc = doc;
563
ctxt->xpathCtxt->namespaces = nsList;
564
ctxt->xpathCtxt->nsNr = nsNr;
565
newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
566
ctxt->xpathCtxt->node = oldnode;
567
ctxt->xpathCtxt->doc = olddoc;
568
ctxt->xpathCtxt->namespaces = oldNamespaces;
569
ctxt->xpathCtxt->nsNr = oldNsNr;
570
ctxt->xpathCtxt->contextSize = oldContextSize;
571
ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
572
if (newlist == NULL)
573
return(-1);
574
if (newlist->type != XPATH_NODESET) {
575
xmlXPathFreeObject(newlist);
576
return(-1);
577
}
578
ix = 0;
579
580
if ((parent == NULL) || (node->doc == NULL) || isRVT)
581
nocache = 1;
582
583
if (nocache == 0) {
584
if (list != NULL)
585
xmlXPathFreeObject(list);
586
list = newlist;
587
588
XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
589
(void *) list;
590
XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
591
(void *) doc;
592
XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
593
0;
594
XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
595
xmlXPathFreeObjectWrapper;
596
} else
597
list = newlist;
598
}
599
if ((list->nodesetval == NULL) ||
600
(list->nodesetval->nodeNr <= 0)) {
601
if (nocache == 1)
602
xmlXPathFreeObject(list);
603
return(0);
604
}
605
/* TODO: store the index and use it for the scan */
606
if (ix == 0) {
607
for (j = 0;j < list->nodesetval->nodeNr;j++) {
608
if (list->nodesetval->nodeTab[j] == node) {
609
if (nocache == 1)
610
xmlXPathFreeObject(list);
611
return(1);
612
}
613
}
614
} else {
615
}
616
if (nocache == 1)
617
xmlXPathFreeObject(list);
618
return(0);
619
}
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
xsltStepOpPtr step) {
634
switch (step->op) {
635
case XSLT_OP_ROOT:
636
if ((node->type == XML_DOCUMENT_NODE) ||
637
#ifdef LIBXML_DOCB_ENABLED
638
(node->type == XML_DOCB_DOCUMENT_NODE) ||
639
#endif
640
(node->type == XML_HTML_DOCUMENT_NODE))
641
return(1);
642
if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
643
return(1);
644
return(0);
645
case XSLT_OP_ELEM:
646
if (node->type != XML_ELEMENT_NODE)
647
return(0);
648
if (step->value == NULL)
649
return(1);
650
if (step->value[0] != node->name[0])
651
return(0);
652
if (!xmlStrEqual(step->value, node->name))
653
return(0);
654
655
/* Namespace test */
656
if (node->ns == NULL) {
657
if (step->value2 != NULL)
658
return(0);
659
} else if (node->ns->href != NULL) {
660
if (step->value2 == NULL)
661
return(0);
662
if (!xmlStrEqual(step->value2, node->ns->href))
663
return(0);
664
}
665
return(1);
666
case XSLT_OP_ATTR:
667
if (node->type != XML_ATTRIBUTE_NODE)
668
return(0);
669
if (step->value != NULL) {
670
if (step->value[0] != node->name[0])
671
return(0);
672
if (!xmlStrEqual(step->value, node->name))
673
return(0);
674
}
675
/* Namespace test */
676
if (node->ns == NULL) {
677
if (step->value2 != NULL)
678
return(0);
679
} else if (step->value2 != NULL) {
680
if (!xmlStrEqual(step->value2, node->ns->href))
681
return(0);
682
}
683
return(1);
684
case XSLT_OP_ID: {
685
/* TODO Handle IDs decently, must be done differently */
686
xmlAttrPtr id;
687
688
if (node->type != XML_ELEMENT_NODE)
689
return(0);
690
691
id = xmlGetID(node->doc, step->value);
692
if ((id == NULL) || (id->parent != node))
693
return(0);
694
break;
695
}
696
case XSLT_OP_KEY: {
697
xmlNodeSetPtr list;
698
int indx;
699
700
list = xsltGetKey(ctxt, step->value,
701
step->value3, step->value2);
702
if (list == NULL)
703
return(0);
704
for (indx = 0;indx < list->nodeNr;indx++)
705
if (list->nodeTab[indx] == node)
706
break;
707
if (indx >= list->nodeNr)
708
return(0);
709
break;
710
}
711
case XSLT_OP_NS:
712
if (node->type != XML_ELEMENT_NODE)
713
return(0);
714
if (node->ns == NULL) {
715
if (step->value != NULL)
716
return(0);
717
} else if (node->ns->href != NULL) {
718
if (step->value == NULL)
719
return(0);
720
if (!xmlStrEqual(step->value, node->ns->href))
721
return(0);
722
}
723
break;
724
case XSLT_OP_ALL:
725
if (node->type != XML_ELEMENT_NODE)
726
return(0);
727
break;
728
case XSLT_OP_PI:
729
if (node->type != XML_PI_NODE)
730
return(0);
731
if (step->value != NULL) {
732
if (!xmlStrEqual(step->value, node->name))
733
return(0);
734
}
735
break;
736
case XSLT_OP_COMMENT:
737
if (node->type != XML_COMMENT_NODE)
738
return(0);
739
break;
740
case XSLT_OP_TEXT:
741
if ((node->type != XML_TEXT_NODE) &&
742
(node->type != XML_CDATA_SECTION_NODE))
743
return(0);
744
break;
745
case XSLT_OP_NODE:
746
switch (node->type) {
747
case XML_ELEMENT_NODE:
748
case XML_CDATA_SECTION_NODE:
749
case XML_PI_NODE:
750
case XML_COMMENT_NODE:
751
case XML_TEXT_NODE:
752
break;
753
default:
754
return(0);
755
}
756
break;
757
default:
758
xsltTransformError(ctxt, NULL, node,
759
"xsltTestStepMatch: unexpected step op %d\n",
760
step->op);
761
return(-1);
762
}
763
764
return(1);
765
}
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
xsltStepOpPtr sel) {
783
xmlNodePtr oldNode;
784
xmlDocPtr doc;
785
int oldCS, oldCP;
786
int pos = 0, len = 0;
787
int isRVT;
788
int match;
789
790
if (step->value == NULL)
791
return(0);
792
if (step->comp == NULL)
793
return(0);
794
if (sel == NULL)
795
return(0);
796
797
doc = node->doc;
798
if (XSLT_IS_RES_TREE_FRAG(doc))
799
isRVT = 1;
800
else
801
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
oldCS = ctxt->xpathCtxt->contextSize;
814
oldCP = ctxt->xpathCtxt->proximityPosition;
815
{
816
xmlNodePtr previous;
817
int nocache = 0;
818
819
previous = (xmlNodePtr)
820
XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
821
if ((previous != NULL) &&
822
(previous->parent == node->parent)) {
823
/*
824
* just walk back to adjust the index
825
*/
826
int indx = 0;
827
xmlNodePtr sibling = node;
828
829
while (sibling != NULL) {
830
if (sibling == previous)
831
break;
832
if (xsltTestStepMatch(ctxt, sibling, sel))
833
indx++;
834
sibling = sibling->prev;
835
}
836
if (sibling == NULL) {
837
/* hum going backward in document order ... */
838
indx = 0;
839
sibling = node;
840
while (sibling != NULL) {
841
if (sibling == previous)
842
break;
843
if (xsltTestStepMatch(ctxt, sibling, sel))
844
indx--;
845
sibling = sibling->next;
846
}
847
}
848
if (sibling != NULL) {
849
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
if (node->doc != NULL) {
856
len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival);
857
if (!isRVT) {
858
XSLT_RUNTIME_EXTRA(ctxt,
859
sel->previousExtra, ptr) = node;
860
XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
861
}
862
}
863
} else
864
pos = 0;
865
} else {
866
/*
867
* recompute the index
868
*/
869
xmlNodePtr parent = node->parent;
870
xmlNodePtr siblings = NULL;
871
872
if (parent) siblings = parent->children;
873
874
while (siblings != NULL) {
875
if (siblings == node) {
876
len++;
877
pos = len;
878
} else if (xsltTestStepMatch(ctxt, siblings, sel)) {
879
len++;
880
}
881
siblings = siblings->next;
882
}
883
if ((parent == NULL) || (node->doc == NULL))
884
nocache = 1;
885
else {
886
while (parent->parent != NULL)
887
parent = parent->parent;
888
if (((parent->type != XML_DOCUMENT_NODE) &&
889
(parent->type != XML_HTML_DOCUMENT_NODE)) ||
890
(parent != (xmlNodePtr) node->doc))
891
nocache = 1;
892
}
893
}
894
if (pos != 0) {
895
ctxt->xpathCtxt->contextSize = len;
896
ctxt->xpathCtxt->proximityPosition = pos;
897
/*
898
* If the node is in a Value Tree we cannot
899
* cache it !
900
*/
901
if ((!isRVT) && (node->doc != NULL) &&
902
(nocache == 0)) {
903
XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
904
XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
905
XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len;
906
}
907
}
908
}
909
910
oldNode = ctxt->node;
911
ctxt->node = node;
912
913
match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr);
914
915
if (pos != 0) {
916
ctxt->xpathCtxt->contextSize = oldCS;
917
ctxt->xpathCtxt->proximityPosition = oldCP;
918
}
919
ctxt->node = oldNode;
920
921
return match;
922
}
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
const xmlChar *modeURI) {
940
int i;
941
int found = 0;
942
xmlNodePtr node = matchNode;
943
xmlNodePtr oldInst;
944
xsltStepOpPtr step, sel = NULL;
945
xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
946
947
if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
948
xsltTransformError(ctxt, NULL, node,
949
"xsltTestCompMatch: null arg\n");
950
return(-1);
951
}
952
if (mode != NULL) {
953
if (comp->mode == NULL)
954
return(0);
955
/*
956
* both mode strings must be interned on the stylesheet dictionary
957
*/
958
if (comp->mode != mode)
959
return(0);
960
} else {
961
if (comp->mode != NULL)
962
return(0);
963
}
964
if (modeURI != NULL) {
965
if (comp->modeURI == NULL)
966
return(0);
967
/*
968
* both modeURI strings must be interned on the stylesheet dictionary
969
*/
970
if (comp->modeURI != modeURI)
971
return(0);
972
} else {
973
if (comp->modeURI != NULL)
974
return(0);
975
}
976
977
/* Some XPath functions rely on inst being set correctly. */
978
oldInst = ctxt->inst;
979
ctxt->inst = comp->node;
980
981
i = 0;
982
restart:
983
for (;i < comp->nbStep;i++) {
984
step = &comp->steps[i];
985
if (step->op != XSLT_OP_PREDICATE)
986
sel = step;
987
switch (step->op) {
988
case XSLT_OP_END:
989
goto found;
990
case XSLT_OP_PARENT:
991
if ((node->type == XML_DOCUMENT_NODE) ||
992
(node->type == XML_HTML_DOCUMENT_NODE) ||
993
#ifdef LIBXML_DOCB_ENABLED
994
(node->type == XML_DOCB_DOCUMENT_NODE) ||
995
#endif
996
(node->type == XML_NAMESPACE_DECL))
997
goto rollback;
998
node = node->parent;
999
if (node == NULL)
1000
goto rollback;
1001
if (step->value == NULL)
1002
continue;
1003
if (step->value[0] != node->name[0])
1004
goto rollback;
1005
if (!xmlStrEqual(step->value, node->name))
1006
goto rollback;
1007
/* Namespace test */
1008
if (node->ns == NULL) {
1009
if (step->value2 != NULL)
1010
goto rollback;
1011
} else if (node->ns->href != NULL) {
1012
if (step->value2 == NULL)
1013
goto rollback;
1014
if (!xmlStrEqual(step->value2, node->ns->href))
1015
goto rollback;
1016
}
1017
continue;
1018
case XSLT_OP_ANCESTOR:
1019
/* TODO: implement coalescing of ANCESTOR/NODE ops */
1020
if (step->value == NULL) {
1021
step = &comp->steps[i+1];
1022
if (step->op == XSLT_OP_ROOT)
1023
goto found;
1024
/* added NS, ID and KEY as a result of bug 168208 */
1025
if ((step->op != XSLT_OP_ELEM) &&
1026
(step->op != XSLT_OP_ALL) &&
1027
(step->op != XSLT_OP_NS) &&
1028
(step->op != XSLT_OP_ID) &&
1029
(step->op != XSLT_OP_KEY))
1030
goto rollback;
1031
}
1032
if (node == NULL)
1033
goto rollback;
1034
if ((node->type == XML_DOCUMENT_NODE) ||
1035
(node->type == XML_HTML_DOCUMENT_NODE) ||
1036
#ifdef LIBXML_DOCB_ENABLED
1037
(node->type == XML_DOCB_DOCUMENT_NODE) ||
1038
#endif
1039
(node->type == XML_NAMESPACE_DECL))
1040
goto rollback;
1041
node = node->parent;
1042
if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
1043
xsltPatPushState(ctxt, &states, i, node);
1044
continue;
1045
}
1046
i++;
1047
sel = step;
1048
if (step->value == NULL) {
1049
xsltPatPushState(ctxt, &states, i - 1, node);
1050
continue;
1051
}
1052
while (node != NULL) {
1053
if ((node->type == XML_ELEMENT_NODE) &&
1054
(step->value[0] == node->name[0]) &&
1055
(xmlStrEqual(step->value, node->name))) {
1056
/* Namespace test */
1057
if (node->ns == NULL) {
1058
if (step->value2 == NULL)
1059
break;
1060
} else if (node->ns->href != NULL) {
1061
if ((step->value2 != NULL) &&
1062
(xmlStrEqual(step->value2, node->ns->href)))
1063
break;
1064
}
1065
}
1066
node = node->parent;
1067
}
1068
if (node == NULL)
1069
goto rollback;
1070
xsltPatPushState(ctxt, &states, i - 1, node);
1071
continue;
1072
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
if (comp->direct) {
1081
found = xsltTestCompMatchDirect(ctxt, comp, matchNode,
1082
comp->nsList, comp->nsNr);
1083
goto exit;
1084
}
1085
1086
if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel))
1087
goto rollback;
1088
1089
break;
1090
}
1091
default:
1092
if (xsltTestStepMatch(ctxt, node, step) != 1)
1093
goto rollback;
1094
break;
1095
}
1096
}
1097
found:
1098
found = 1;
1099
exit:
1100
ctxt->inst = oldInst;
1101
if (states.states != NULL) {
1102
/* Free the rollback states */
1103
xmlFree(states.states);
1104
}
1105
return found;
1106
rollback:
1107
/* got an error try to rollback */
1108
if (states.states == NULL || states.nbstates <= 0) {
1109
found = 0;
1110
goto exit;
1111
}
1112
states.nbstates--;
1113
i = states.states[states.nbstates].step;
1114
node = states.states[states.nbstates].node;
1115
#if 0
1116
fprintf(stderr, "Pop: %d, %s\n", i, node->name);
1117
#endif
1118
goto restart;
1119
}
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
xsltCompMatchPtr comp) {
1134
int ret;
1135
1136
if ((ctxt == NULL) || (node == NULL))
1137
return(-1);
1138
while (comp != NULL) {
1139
ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
1140
if (ret == 1)
1141
return(1);
1142
comp = comp->next;
1143
}
1144
return(0);
1145
}
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
xsltCompMatchClearCache(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp) {
1156
xsltStepOpPtr sel;
1157
xmlXPathObjectPtr list;
1158
1159
if ((ctxt == NULL) || (comp == NULL))
1160
return;
1161
1162
sel = &comp->steps[0];
1163
list = (xmlXPathObjectPtr) XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
1164
1165
if (list != NULL) {
1166
xmlXPathFreeObject(list);
1167
1168
XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = NULL;
1169
XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = NULL;
1170
XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = 0;
1171
XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = NULL;
1172
}
1173
}
1174
1175
/************************************************************************
1176
* *
1177
* Dedicated parser for templates *
1178
* *
1179
************************************************************************/
1180
1181
#define CUR (*ctxt->cur)
1182
#define SKIP(val) ctxt->cur += (val)
1183
#define NXT(val) ctxt->cur[(val)]
1184
#define CUR_PTR ctxt->cur
1185
1186
#define SKIP_BLANKS \
1187
while (xmlIsBlank_ch(CUR)) NEXT
1188
1189
#define CURRENT (*ctxt->cur)
1190
#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1191
1192
1193
#define PUSH(op, val, val2, novar) \
1194
if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1195
1196
#define SWAP() \
1197
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
xsltScanLiteral(xsltParserContextPtr ctxt) {
1221
const xmlChar *q, *cur;
1222
xmlChar *ret = NULL;
1223
int val, len;
1224
1225
SKIP_BLANKS;
1226
if (CUR == '"') {
1227
NEXT;
1228
cur = q = CUR_PTR;
1229
val = xsltGetUTF8CharZ(cur, &len);
1230
while ((xmlIsCharQ(val)) && (val != '"')) {
1231
cur += len;
1232
val = xsltGetUTF8CharZ(cur, &len);
1233
}
1234
if (!xmlIsCharQ(val)) {
1235
ctxt->error = 1;
1236
return(NULL);
1237
} else {
1238
ret = xmlStrndup(q, cur - q);
1239
}
1240
cur += len;
1241
CUR_PTR = cur;
1242
} else if (CUR == '\'') {
1243
NEXT;
1244
cur = q = CUR_PTR;
1245
val = xsltGetUTF8CharZ(cur, &len);
1246
while ((xmlIsCharQ(val)) && (val != '\'')) {
1247
cur += len;
1248
val = xsltGetUTF8CharZ(cur, &len);
1249
}
1250
if (!xmlIsCharQ(val)) {
1251
ctxt->error = 1;
1252
return(NULL);
1253
} else {
1254
ret = xmlStrndup(q, cur - q);
1255
}
1256
cur += len;
1257
CUR_PTR = cur;
1258
} else {
1259
ctxt->error = 1;
1260
return(NULL);
1261
}
1262
return(ret);
1263
}
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
xsltScanNCName(xsltParserContextPtr ctxt) {
1276
const xmlChar *q, *cur;
1277
xmlChar *ret = NULL;
1278
int val, len;
1279
1280
SKIP_BLANKS;
1281
1282
cur = q = CUR_PTR;
1283
val = xsltGetUTF8CharZ(cur, &len);
1284
if (!xmlIsBaseCharQ(val) && !xmlIsIdeographicQ(val) && (val != '_'))
1285
return(NULL);
1286
1287
while (xmlIsBaseCharQ(val) || xmlIsIdeographicQ(val) ||
1288
xmlIsDigitQ(val) ||
1289
(val == '.') || (val == '-') ||
1290
(val == '_') ||
1291
xmlIsCombiningQ(val) ||
1292
xmlIsExtenderQ(val)) {
1293
cur += len;
1294
val = xsltGetUTF8CharZ(cur, &len);
1295
}
1296
ret = xmlStrndup(q, cur - q);
1297
CUR_PTR = cur;
1298
return(ret);
1299
}
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
int aid, int novar, xsltAxis axis) {
1321
xmlChar *lit = NULL;
1322
xmlChar *lit2 = NULL;
1323
1324
if (CUR != '(') {
1325
xsltTransformError(NULL, NULL, NULL,
1326
"xsltCompileIdKeyPattern : ( expected\n");
1327
ctxt->error = 1;
1328
return;
1329
}
1330
if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
1331
if (axis != 0) {
1332
xsltTransformError(NULL, NULL, NULL,
1333
"xsltCompileIdKeyPattern : NodeTest expected\n");
1334
ctxt->error = 1;
1335
return;
1336
}
1337
NEXT;
1338
SKIP_BLANKS;
1339
lit = xsltScanLiteral(ctxt);
1340
if (ctxt->error) {
1341
xsltTransformError(NULL, NULL, NULL,
1342
"xsltCompileIdKeyPattern : Literal expected\n");
1343
xmlFree(lit);
1344
return;
1345
}
1346
SKIP_BLANKS;
1347
if (CUR != ')') {
1348
xsltTransformError(NULL, NULL, NULL,
1349
"xsltCompileIdKeyPattern : ) expected\n");
1350
xmlFree(lit);
1351
ctxt->error = 1;
1352
return;
1353
}
1354
NEXT;
1355
PUSH(XSLT_OP_ID, lit, NULL, novar);
1356
lit = NULL;
1357
} else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
1358
if (axis != 0) {
1359
xsltTransformError(NULL, NULL, NULL,
1360
"xsltCompileIdKeyPattern : NodeTest expected\n");
1361
ctxt->error = 1;
1362
return;
1363
}
1364
NEXT;
1365
SKIP_BLANKS;
1366
lit = xsltScanLiteral(ctxt);
1367
if (ctxt->error) {
1368
xsltTransformError(NULL, NULL, NULL,
1369
"xsltCompileIdKeyPattern : Literal expected\n");
1370
xmlFree(lit);
1371
return;
1372
}
1373
SKIP_BLANKS;
1374
if (CUR != ',') {
1375
xsltTransformError(NULL, NULL, NULL,
1376
"xsltCompileIdKeyPattern : , expected\n");
1377
xmlFree(lit);
1378
ctxt->error = 1;
1379
return;
1380
}
1381
NEXT;
1382
SKIP_BLANKS;
1383
lit2 = xsltScanLiteral(ctxt);
1384
if (ctxt->error) {
1385
xsltTransformError(NULL, NULL, NULL,
1386
"xsltCompileIdKeyPattern : Literal expected\n");
1387
xmlFree(lit);
1388
return;
1389
}
1390
SKIP_BLANKS;
1391
if (CUR != ')') {
1392
xsltTransformError(NULL, NULL, NULL,
1393
"xsltCompileIdKeyPattern : ) expected\n");
1394
xmlFree(lit);
1395
xmlFree(lit2);
1396
ctxt->error = 1;
1397
return;
1398
}
1399
NEXT;
1400
/* URGENT TODO: support namespace in keys */
1401
PUSH(XSLT_OP_KEY, lit, lit2, novar);
1402
lit = NULL;
1403
lit2 = NULL;
1404
} else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
1405
NEXT;
1406
SKIP_BLANKS;
1407
if (CUR != ')') {
1408
lit = xsltScanLiteral(ctxt);
1409
if (ctxt->error) {
1410
xsltTransformError(NULL, NULL, NULL,
1411
"xsltCompileIdKeyPattern : Literal expected\n");
1412
xmlFree(lit);
1413
return;
1414
}
1415
SKIP_BLANKS;
1416
if (CUR != ')') {
1417
xsltTransformError(NULL, NULL, NULL,
1418
"xsltCompileIdKeyPattern : ) expected\n");
1419
ctxt->error = 1;
1420
xmlFree(lit);
1421
return;
1422
}
1423
}
1424
NEXT;
1425
PUSH(XSLT_OP_PI, lit, NULL, novar);
1426
lit = NULL;
1427
} else if (xmlStrEqual(name, (const xmlChar *)"text")) {
1428
NEXT;
1429
SKIP_BLANKS;
1430
if (CUR != ')') {
1431
xsltTransformError(NULL, NULL, NULL,
1432
"xsltCompileIdKeyPattern : ) expected\n");
1433
ctxt->error = 1;
1434
return;
1435
}
1436
NEXT;
1437
PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
1438
} else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
1439
NEXT;
1440
SKIP_BLANKS;
1441
if (CUR != ')') {
1442
xsltTransformError(NULL, NULL, NULL,
1443
"xsltCompileIdKeyPattern : ) expected\n");
1444
ctxt->error = 1;
1445
return;
1446
}
1447
NEXT;
1448
PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
1449
} else if (xmlStrEqual(name, (const xmlChar *)"node")) {
1450
NEXT;
1451
SKIP_BLANKS;
1452
if (CUR != ')') {
1453
xsltTransformError(NULL, NULL, NULL,
1454
"xsltCompileIdKeyPattern : ) expected\n");
1455
ctxt->error = 1;
1456
return;
1457
}
1458
NEXT;
1459
if (axis == AXIS_ATTRIBUTE) {
1460
PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1461
}
1462
else {
1463
PUSH(XSLT_OP_NODE, NULL, NULL, novar);
1464
}
1465
} else if (aid) {
1466
xsltTransformError(NULL, NULL, NULL,
1467
"xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1468
ctxt->error = 1;
1469
return;
1470
} else {
1471
xsltTransformError(NULL, NULL, NULL,
1472
"xsltCompileIdKeyPattern : node type\n");
1473
ctxt->error = 1;
1474
return;
1475
}
1476
error:
1477
return;
1478
}
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
xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1504
xmlChar *name = NULL;
1505
const xmlChar *URI = NULL;
1506
xmlChar *URL = NULL;
1507
xmlChar *ret = NULL;
1508
int level;
1509
xsltAxis axis = 0;
1510
1511
SKIP_BLANKS;
1512
if ((token == NULL) && (CUR == '@')) {
1513
NEXT;
1514
axis = AXIS_ATTRIBUTE;
1515
}
1516
parse_node_test:
1517
if (token == NULL)
1518
token = xsltScanNCName(ctxt);
1519
if (token == NULL) {
1520
if (CUR == '*') {
1521
NEXT;
1522
if (axis == AXIS_ATTRIBUTE) {
1523
PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1524
}
1525
else {
1526
PUSH(XSLT_OP_ALL, NULL, NULL, novar);
1527
}
1528
goto parse_predicate;
1529
} else {
1530
xsltTransformError(NULL, NULL, NULL,
1531
"xsltCompileStepPattern : Name expected\n");
1532
ctxt->error = 1;
1533
goto error;
1534
}
1535
}
1536
1537
1538
SKIP_BLANKS;
1539
if (CUR == '(') {
1540
xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
1541
xmlFree(token);
1542
token = NULL;
1543
if (ctxt->error)
1544
goto error;
1545
} else if (CUR == ':') {
1546
NEXT;
1547
if (CUR != ':') {
1548
xmlChar *prefix = token;
1549
xmlNsPtr ns;
1550
1551
/*
1552
* This is a namespace match
1553
*/
1554
token = xsltScanNCName(ctxt);
1555
ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1556
if (ns == NULL) {
1557
xsltTransformError(NULL, NULL, NULL,
1558
"xsltCompileStepPattern : no namespace bound to prefix %s\n",
1559
prefix);
1560
xmlFree(prefix);
1561
prefix=NULL;
1562
ctxt->error = 1;
1563
goto error;
1564
} else {
1565
URL = xmlStrdup(ns->href);
1566
}
1567
xmlFree(prefix);
1568
prefix=NULL;
1569
if (token == NULL) {
1570
if (CUR == '*') {
1571
NEXT;
1572
if (axis == AXIS_ATTRIBUTE) {
1573
PUSH(XSLT_OP_ATTR, NULL, URL, novar);
1574
URL = NULL;
1575
}
1576
else {
1577
PUSH(XSLT_OP_NS, URL, NULL, novar);
1578
URL = NULL;
1579
}
1580
} else {
1581
xsltTransformError(NULL, NULL, NULL,
1582
"xsltCompileStepPattern : Name expected\n");
1583
ctxt->error = 1;
1584
goto error;
1585
}
1586
} else {
1587
if (axis == AXIS_ATTRIBUTE) {
1588
PUSH(XSLT_OP_ATTR, token, URL, novar);
1589
token = NULL;
1590
URL = NULL;
1591
}
1592
else {
1593
PUSH(XSLT_OP_ELEM, token, URL, novar);
1594
token = NULL;
1595
URL = NULL;
1596
}
1597
}
1598
} else {
1599
if (axis != 0) {
1600
xsltTransformError(NULL, NULL, NULL,
1601
"xsltCompileStepPattern : NodeTest expected\n");
1602
ctxt->error = 1;
1603
goto error;
1604
}
1605
NEXT;
1606
if (xmlStrEqual(token, (const xmlChar *) "child")) {
1607
axis = AXIS_CHILD;
1608
} else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1609
axis = AXIS_ATTRIBUTE;
1610
} else {
1611
xsltTransformError(NULL, NULL, NULL,
1612
"xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1613
ctxt->error = 1;
1614
goto error;
1615
}
1616
xmlFree(token);
1617
token = NULL;
1618
SKIP_BLANKS;
1619
token = xsltScanNCName(ctxt);
1620
goto parse_node_test;
1621
}
1622
} else {
1623
URI = xsltGetQNameURI(ctxt->elem, &token);
1624
if (token == NULL) {
1625
ctxt->error = 1;
1626
goto error;
1627
}
1628
if (URI != NULL)
1629
URL = xmlStrdup(URI);
1630
if (axis == AXIS_ATTRIBUTE) {
1631
PUSH(XSLT_OP_ATTR, token, URL, novar);
1632
token = NULL;
1633
URL = NULL;
1634
}
1635
else {
1636
PUSH(XSLT_OP_ELEM, token, URL, novar);
1637
token = NULL;
1638
URL = NULL;
1639
}
1640
}
1641
parse_predicate:
1642
SKIP_BLANKS;
1643
level = 0;
1644
while (CUR == '[') {
1645
const xmlChar *q;
1646
1647
level++;
1648
NEXT;
1649
q = CUR_PTR;
1650
while (CUR != 0) {
1651
/* Skip over nested predicates */
1652
if (CUR == '[')
1653
level++;
1654
else if (CUR == ']') {
1655
level--;
1656
if (level == 0)
1657
break;
1658
} else if (CUR == '"') {
1659
NEXT;
1660
while ((CUR != 0) && (CUR != '"'))
1661
NEXT;
1662
} else if (CUR == '\'') {
1663
NEXT;
1664
while ((CUR != 0) && (CUR != '\''))
1665
NEXT;
1666
}
1667
NEXT;
1668
}
1669
if (CUR == 0) {
1670
xsltTransformError(NULL, NULL, NULL,
1671
"xsltCompileStepPattern : ']' expected\n");
1672
ctxt->error = 1;
1673
return;
1674
}
1675
ret = xmlStrndup(q, CUR_PTR - q);
1676
PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
1677
ret = NULL;
1678
/* push the predicate lower than local test */
1679
SWAP();
1680
NEXT;
1681
SKIP_BLANKS;
1682
}
1683
return;
1684
error:
1685
if (token != NULL)
1686
xmlFree(token);
1687
if (name != NULL)
1688
xmlFree(name);
1689
if (URL != NULL)
1690
xmlFree(URL);
1691
if (ret != NULL)
1692
xmlFree(ret);
1693
}
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
xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1710
xsltCompileStepPattern(ctxt, token, novar);
1711
if (ctxt->error)
1712
goto error;
1713
SKIP_BLANKS;
1714
while ((CUR != 0) && (CUR != '|')) {
1715
if ((CUR == '/') && (NXT(1) == '/')) {
1716
PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1717
NEXT;
1718
NEXT;
1719
SKIP_BLANKS;
1720
xsltCompileStepPattern(ctxt, NULL, novar);
1721
} else if (CUR == '/') {
1722
PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1723
NEXT;
1724
SKIP_BLANKS;
1725
xsltCompileStepPattern(ctxt, NULL, novar);
1726
} else {
1727
ctxt->error = 1;
1728
}
1729
if (ctxt->error)
1730
goto error;
1731
SKIP_BLANKS;
1732
}
1733
error:
1734
return;
1735
}
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
xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
1751
SKIP_BLANKS;
1752
if ((CUR == '/') && (NXT(1) == '/')) {
1753
/*
1754
* since we reverse the query
1755
* a leading // can be safely ignored
1756
*/
1757
NEXT;
1758
NEXT;
1759
ctxt->comp->priority = 0.5; /* '//' means not 0 priority */
1760
xsltCompileRelativePathPattern(ctxt, NULL, novar);
1761
} else if (CUR == '/') {
1762
/*
1763
* We need to find root as the parent
1764
*/
1765
NEXT;
1766
SKIP_BLANKS;
1767
PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
1768
if ((CUR != 0) && (CUR != '|')) {
1769
PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1770
xsltCompileRelativePathPattern(ctxt, NULL, novar);
1771
}
1772
} else if (CUR == '*') {
1773
xsltCompileRelativePathPattern(ctxt, NULL, novar);
1774
} else if (CUR == '@') {
1775
xsltCompileRelativePathPattern(ctxt, NULL, novar);
1776
} else {
1777
xmlChar *name;
1778
name = xsltScanNCName(ctxt);
1779
if (name == NULL) {
1780
xsltTransformError(NULL, NULL, NULL,
1781
"xsltCompileLocationPathPattern : Name expected\n");
1782
ctxt->error = 1;
1783
return;
1784
}
1785
SKIP_BLANKS;
1786
if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
1787
xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
1788
xmlFree(name);
1789
name = NULL;
1790
if (ctxt->error)
1791
return;
1792
if ((CUR == '/') && (NXT(1) == '/')) {
1793
PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1794
NEXT;
1795
NEXT;
1796
SKIP_BLANKS;
1797
xsltCompileRelativePathPattern(ctxt, NULL, novar);
1798
} else if (CUR == '/') {
1799
PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1800
NEXT;
1801
SKIP_BLANKS;
1802
xsltCompileRelativePathPattern(ctxt, NULL, novar);
1803
}
1804
return;
1805
}
1806
xsltCompileRelativePathPattern(ctxt, name, novar);
1807
}
1808
error:
1809
return;
1810
}
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
xsltTransformContextPtr runtime, int novar) {
1833
xsltParserContextPtr ctxt = NULL;
1834
xsltCompMatchPtr element, first = NULL, previous = NULL;
1835
int current, start, end, level, j;
1836
1837
if (pattern == NULL) {
1838
xsltTransformError(NULL, NULL, node,
1839
"xsltCompilePattern : NULL pattern\n");
1840
return(NULL);
1841
}
1842
1843
ctxt = xsltNewParserContext(style, runtime);
1844
if (ctxt == NULL)
1845
return(NULL);
1846
ctxt->doc = doc;
1847
ctxt->elem = node;
1848
current = end = 0;
1849
while (pattern[current] != 0) {
1850
start = current;
1851
while (xmlIsBlank_ch(pattern[current]))
1852
current++;
1853
end = current;
1854
level = 0;
1855
while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
1856
if (pattern[end] == '[')
1857
level++;
1858
else if (pattern[end] == ']')
1859
level--;
1860
else if (pattern[end] == '\'') {
1861
end++;
1862
while ((pattern[end] != 0) && (pattern[end] != '\''))
1863
end++;
1864
} else if (pattern[end] == '"') {
1865
end++;
1866
while ((pattern[end] != 0) && (pattern[end] != '"'))
1867
end++;
1868
}
1869
if (pattern[end] == 0)
1870
break;
1871
end++;
1872
}
1873
if (current == end) {
1874
xsltTransformError(NULL, NULL, node,
1875
"xsltCompilePattern : NULL pattern\n");
1876
goto error;
1877
}
1878
element = xsltNewCompMatch();
1879
if (element == NULL) {
1880
goto error;
1881
}
1882
if (first == NULL)
1883
first = element;
1884
else if (previous != NULL)
1885
previous->next = element;
1886
previous = element;
1887
1888
ctxt->comp = element;
1889
ctxt->base = xmlStrndup(&pattern[start], end - start);
1890
if (ctxt->base == NULL)
1891
goto error;
1892
ctxt->cur = &(ctxt->base)[current - start];
1893
element->pattern = ctxt->base;
1894
element->node = node;
1895
element->nsList = xmlGetNsList(doc, node);
1896
j = 0;
1897
if (element->nsList != NULL) {
1898
while (element->nsList[j] != NULL)
1899
j++;
1900
}
1901
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
element->priority = 0;
1914
xsltCompileLocationPathPattern(ctxt, novar);
1915
if (ctxt->error) {
1916
xsltTransformError(NULL, style, node,
1917
"xsltCompilePattern : failed to compile '%s'\n",
1918
element->pattern);
1919
if (style != NULL) style->errors++;
1920
goto error;
1921
}
1922
1923
/*
1924
* Reverse for faster interpretation.
1925
*/
1926
xsltReverseCompMatch(ctxt, element);
1927
1928
/*
1929
* Set-up the priority
1930
*/
1931
if (element->priority == 0) { /* if not yet determined */
1932
if (((element->steps[0].op == XSLT_OP_ELEM) ||
1933
(element->steps[0].op == XSLT_OP_ATTR) ||
1934
(element->steps[0].op == XSLT_OP_PI)) &&
1935
(element->steps[0].value != NULL) &&
1936
(element->steps[1].op == XSLT_OP_END)) {
1937
; /* previously preset */
1938
} else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1939
(element->steps[0].value2 != NULL) &&
1940
(element->steps[1].op == XSLT_OP_END)) {
1941
element->priority = -0.25;
1942
} else if ((element->steps[0].op == XSLT_OP_NS) &&
1943
(element->steps[0].value != NULL) &&
1944
(element->steps[1].op == XSLT_OP_END)) {
1945
element->priority = -0.25;
1946
} else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1947
(element->steps[0].value == NULL) &&
1948
(element->steps[0].value2 == NULL) &&
1949
(element->steps[1].op == XSLT_OP_END)) {
1950
element->priority = -0.5;
1951
} else if (((element->steps[0].op == XSLT_OP_PI) ||
1952
(element->steps[0].op == XSLT_OP_TEXT) ||
1953
(element->steps[0].op == XSLT_OP_ALL) ||
1954
(element->steps[0].op == XSLT_OP_NODE) ||
1955
(element->steps[0].op == XSLT_OP_COMMENT)) &&
1956
(element->steps[1].op == XSLT_OP_END)) {
1957
element->priority = -0.5;
1958
} else {
1959
element->priority = 0.5;
1960
}
1961
}
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
if (pattern[end] == '|')
1968
end++;
1969
current = end;
1970
}
1971
if (end == 0) {
1972
xsltTransformError(NULL, style, node,
1973
"xsltCompilePattern : NULL pattern\n");
1974
if (style != NULL) style->errors++;
1975
goto error;
1976
}
1977
1978
xsltFreeParserContext(ctxt);
1979
return(first);
1980
1981
error:
1982
if (ctxt != NULL)
1983
xsltFreeParserContext(ctxt);
1984
if (first != NULL)
1985
xsltFreeCompMatchList(first);
1986
return(NULL);
1987
}
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
xsltTransformContextPtr runtime) {
2009
return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0));
2010
}
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
const xmlChar *mode, const xmlChar *modeURI) {
2032
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
xsltCompMatchPtr *top = NULL;
2038
const xmlChar *name = NULL;
2039
float priority; /* the priority */
2040
2041
if ((style == NULL) || (cur == NULL))
2042
return(-1);
2043
2044
if (cur->next != NULL)
2045
cur->position = cur->next->position + 1;
2046
2047
/* Register named template */
2048
if (cur->name != NULL) {
2049
if (style->namedTemplates == NULL) {
2050
style->namedTemplates = xmlHashCreate(10);
2051
if (style->namedTemplates == NULL)
2052
return(-1);
2053
}
2054
else {
2055
void *dup = xmlHashLookup2(style->namedTemplates, cur->name,
2056
cur->nameURI);
2057
if (dup != NULL) {
2058
xsltTransformError(NULL, style, cur->elem,
2059
"xsl:template: error duplicate name '%s'\n",
2060
cur->name);
2061
style->errors++;
2062
return(-1);
2063
}
2064
}
2065
2066
xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur);
2067
}
2068
2069
if (cur->match == NULL) {
2070
if (cur->name == NULL) {
2071
xsltTransformError(NULL, style, cur->elem,
2072
"xsl:template: need to specify match or name attribute\n");
2073
style->errors++;
2074
return(-1);
2075
}
2076
return(0);
2077
}
2078
2079
priority = cur->priority;
2080
pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem,
2081
style, NULL, 1);
2082
if (pat == NULL)
2083
return(-1);
2084
while (pat) {
2085
int success = 0;
2086
2087
next = pat->next;
2088
pat->next = NULL;
2089
name = NULL;
2090
2091
pat->template = cur;
2092
if (mode != NULL)
2093
pat->mode = xmlDictLookup(style->dict, mode, -1);
2094
if (modeURI != NULL)
2095
pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
2096
if (priority != XSLT_PAT_NO_PRIORITY)
2097
pat->priority = priority;
2098
2099
/*
2100
* insert it in the hash table list corresponding to its lookup name
2101
*/
2102
switch (pat->steps[0].op) {
2103
case XSLT_OP_ATTR:
2104
if (pat->steps[0].value != NULL)
2105
name = pat->steps[0].value;
2106
else
2107
top = &(style->attrMatch);
2108
break;
2109
case XSLT_OP_PARENT:
2110
case XSLT_OP_ANCESTOR:
2111
top = &(style->elemMatch);
2112
break;
2113
case XSLT_OP_ROOT:
2114
top = &(style->rootMatch);
2115
break;
2116
case XSLT_OP_KEY:
2117
top = &(style->keyMatch);
2118
break;
2119
case XSLT_OP_ID:
2120
/* TODO optimize ID !!! */
2121
case XSLT_OP_NS:
2122
case XSLT_OP_ALL:
2123
top = &(style->elemMatch);
2124
break;
2125
case XSLT_OP_END:
2126
case XSLT_OP_PREDICATE:
2127
xsltTransformError(NULL, style, NULL,
2128
"xsltAddTemplate: invalid compiled pattern\n");
2129
xsltFreeCompMatch(pat);
2130
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
case XSLT_OP_PI:
2136
if (pat->steps[0].value != NULL)
2137
name = pat->steps[0].value;
2138
else
2139
top = &(style->piMatch);
2140
break;
2141
case XSLT_OP_COMMENT:
2142
top = &(style->commentMatch);
2143
break;
2144
case XSLT_OP_TEXT:
2145
top = &(style->textMatch);
2146
break;
2147
case XSLT_OP_ELEM:
2148
case XSLT_OP_NODE:
2149
if (pat->steps[0].value != NULL)
2150
name = pat->steps[0].value;
2151
else
2152
top = &(style->elemMatch);
2153
break;
2154
}
2155
if (name != NULL) {
2156
if (style->templatesHash == NULL) {
2157
style->templatesHash = xmlHashCreate(1024);
2158
success = (style->templatesHash != NULL) &&
2159
(xmlHashAddEntry3(style->templatesHash, name, mode,
2160
modeURI, pat) >= 0);
2161
} else {
2162
list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
2163
name, mode, modeURI);
2164
if (list == NULL) {
2165
success = (xmlHashAddEntry3(style->templatesHash, name,
2166
mode, modeURI, pat) >= 0);
2167
} 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
if (list->priority <= pat->priority) {
2174
pat->next = list;
2175
xmlHashUpdateEntry3(style->templatesHash, name,
2176
mode, modeURI, pat, NULL);
2177
} else {
2178
while (list->next != NULL) {
2179
if (list->next->priority <= pat->priority)
2180
break;
2181
list = list->next;
2182
}
2183
pat->next = list->next;
2184
list->next = pat;
2185
}
2186
success = 1;
2187
}
2188
}
2189
} else if (top != NULL) {
2190
list = *top;
2191
if (list == NULL) {
2192
*top = pat;
2193
pat->next = NULL;
2194
} else if (list->priority <= pat->priority) {
2195
pat->next = list;
2196
*top = pat;
2197
} else {
2198
while (list->next != NULL) {
2199
if (list->next->priority <= pat->priority)
2200
break;
2201
list = list->next;
2202
}
2203
pat->next = list->next;
2204
list->next = pat;
2205
}
2206
success = 1;
2207
}
2208
if (success == 0) {
2209
xsltTransformError(NULL, style, NULL,
2210
"xsltAddTemplate: invalid compiled pattern\n");
2211
xsltFreeCompMatch(pat);
2212
xsltFreeCompMatchList(next);
2213
return(-1);
2214
}
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
pat = next;
2227
}
2228
return(0);
2229
}
2230
2231
static int
2232
xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
2233
{
2234
if ((ctxt == NULL) || (contextNode == NULL)) {
2235
xsltTransformError(ctxt, NULL, ctxt->inst,
2236
"Internal error in xsltComputeAllKeys(): "
2237
"Bad arguments.\n");
2238
return(-1);
2239
}
2240
2241
if (ctxt->document == NULL) {
2242
/*
2243
* The document info will only be NULL if we have a RTF.
2244
*/
2245
if (contextNode->doc->_private != NULL)
2246
goto doc_info_mismatch;
2247
/*
2248
* On-demand creation of the document info (needed for keys).
2249
*/
2250
ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
2251
if (ctxt->document == NULL)
2252
return(-1);
2253
}
2254
return xsltInitAllDocKeys(ctxt);
2255
2256
doc_info_mismatch:
2257
xsltTransformError(ctxt, NULL, ctxt->inst,
2258
"Internal error in xsltComputeAllKeys(): "
2259
"The context's document info doesn't match the "
2260
"document info of the current result tree.\n");
2261
ctxt->state = XSLT_STATE_STOPPED;
2262
return(-1);
2263
}
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
{
2280
xsltStylesheetPtr curstyle;
2281
xsltTemplatePtr ret = NULL;
2282
const xmlChar *name = NULL;
2283
xsltCompMatchPtr list = NULL;
2284
float priority;
2285
2286
if ((ctxt == NULL) || (node == NULL))
2287
return(NULL);
2288
2289
if (style == NULL) {
2290
curstyle = ctxt->style;
2291
} else {
2292
curstyle = xsltNextImport(style);
2293
}
2294
2295
while ((curstyle != NULL) && (curstyle != style)) {
2296
priority = XSLT_PAT_NO_PRIORITY;
2297
/* TODO : handle IDs/keys here ! */
2298
if (curstyle->templatesHash != NULL) {
2299
/*
2300
* Use the top name as selector
2301
*/
2302
switch (node->type) {
2303
case XML_ELEMENT_NODE:
2304
if (node->name[0] == ' ')
2305
break;
2306
/* Intentional fall-through */
2307
case XML_ATTRIBUTE_NODE:
2308
case XML_PI_NODE:
2309
name = node->name;
2310
break;
2311
case XML_DOCUMENT_NODE:
2312
case XML_HTML_DOCUMENT_NODE:
2313
case XML_TEXT_NODE:
2314
case XML_CDATA_SECTION_NODE:
2315
case XML_COMMENT_NODE:
2316
case XML_ENTITY_REF_NODE:
2317
case XML_ENTITY_NODE:
2318
case XML_DOCUMENT_TYPE_NODE:
2319
case XML_DOCUMENT_FRAG_NODE:
2320
case XML_NOTATION_NODE:
2321
case XML_DTD_NODE:
2322
case XML_ELEMENT_DECL:
2323
case XML_ATTRIBUTE_DECL:
2324
case XML_ENTITY_DECL:
2325
case XML_NAMESPACE_DECL:
2326
case XML_XINCLUDE_START:
2327
case XML_XINCLUDE_END:
2328
break;
2329
default:
2330
return(NULL);
2331
2332
}
2333
}
2334
if (name != NULL) {
2335
/*
2336
* find the list of applicable expressions based on the name
2337
*/
2338
list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
2339
name, ctxt->mode, ctxt->modeURI);
2340
} else
2341
list = NULL;
2342
while (list != NULL) {
2343
if (xsltTestCompMatch(ctxt, list, node,
2344
ctxt->mode, ctxt->modeURI) == 1) {
2345
ret = list->template;
2346
priority = list->priority;
2347
break;
2348
}
2349
list = list->next;
2350
}
2351
list = NULL;
2352
2353
/*
2354
* find alternate generic matches
2355
*/
2356
switch (node->type) {
2357
case XML_ELEMENT_NODE:
2358
if (node->name[0] == ' ')
2359
list = curstyle->rootMatch;
2360
else
2361
list = curstyle->elemMatch;
2362
break;
2363
case XML_ATTRIBUTE_NODE: {
2364
list = curstyle->attrMatch;
2365
break;
2366
}
2367
case XML_PI_NODE:
2368
list = curstyle->piMatch;
2369
break;
2370
case XML_DOCUMENT_NODE:
2371
case XML_HTML_DOCUMENT_NODE: {
2372
list = curstyle->rootMatch;
2373
break;
2374
}
2375
case XML_TEXT_NODE:
2376
case XML_CDATA_SECTION_NODE:
2377
list = curstyle->textMatch;
2378
break;
2379
case XML_COMMENT_NODE:
2380
list = curstyle->commentMatch;
2381
break;
2382
case XML_ENTITY_REF_NODE:
2383
case XML_ENTITY_NODE:
2384
case XML_DOCUMENT_TYPE_NODE:
2385
case XML_DOCUMENT_FRAG_NODE:
2386
case XML_NOTATION_NODE:
2387
case XML_DTD_NODE:
2388
case XML_ELEMENT_DECL:
2389
case XML_ATTRIBUTE_DECL:
2390
case XML_ENTITY_DECL:
2391
case XML_NAMESPACE_DECL:
2392
case XML_XINCLUDE_START:
2393
case XML_XINCLUDE_END:
2394
break;
2395
default:
2396
break;
2397
}
2398
while ((list != NULL) &&
2399
((ret == NULL) ||
2400
(list->priority > priority) ||
2401
((list->priority == priority) &&
2402
(list->template->position > ret->position)))) {
2403
if (xsltTestCompMatch(ctxt, list, node,
2404
ctxt->mode, ctxt->modeURI) == 1) {
2405
ret = list->template;
2406
priority = list->priority;
2407
break;
2408
}
2409
list = list->next;
2410
}
2411
/*
2412
* Some of the tests for elements can also apply to documents
2413
*/
2414
if ((node->type == XML_DOCUMENT_NODE) ||
2415
(node->type == XML_HTML_DOCUMENT_NODE) ||
2416
(node->type == XML_TEXT_NODE)) {
2417
list = curstyle->elemMatch;
2418
while ((list != NULL) &&
2419
((ret == NULL) ||
2420
(list->priority > priority) ||
2421
((list->priority == priority) &&
2422
(list->template->position > ret->position)))) {
2423
if (xsltTestCompMatch(ctxt, list, node,
2424
ctxt->mode, ctxt->modeURI) == 1) {
2425
ret = list->template;
2426
priority = list->priority;
2427
break;
2428
}
2429
list = list->next;
2430
}
2431
} else if ((node->type == XML_PI_NODE) ||
2432
(node->type == XML_COMMENT_NODE)) {
2433
list = curstyle->elemMatch;
2434
while ((list != NULL) &&
2435
((ret == NULL) ||
2436
(list->priority > priority) ||
2437
((list->priority == priority) &&
2438
(list->template->position > ret->position)))) {
2439
if (xsltTestCompMatch(ctxt, list, node,
2440
ctxt->mode, ctxt->modeURI) == 1) {
2441
ret = list->template;
2442
priority = list->priority;
2443
break;
2444
}
2445
list = list->next;
2446
}
2447
}
2448
2449
keyed_match:
2450
if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY) {
2451
list = curstyle->keyMatch;
2452
while ((list != NULL) &&
2453
((ret == NULL) ||
2454
(list->priority > priority) ||
2455
((list->priority == priority) &&
2456
(list->template->position > ret->position)))) {
2457
if (xsltTestCompMatch(ctxt, list, node,
2458
ctxt->mode, ctxt->modeURI) == 1) {
2459
ret = list->template;
2460
priority = list->priority;
2461
break;
2462
}
2463
list = list->next;
2464
}
2465
}
2466
else if (ctxt->hasTemplKeyPatterns &&
2467
((ctxt->document == NULL) ||
2468
(ctxt->document->nbKeysComputed < ctxt->nbKeys)))
2469
{
2470
/*
2471
* Compute all remaining keys for this document.
2472
*
2473
* REVISIT TODO: I think this could be further optimized.
2474
*/
2475
if (xsltComputeAllKeys(ctxt, node) == -1)
2476
goto error;
2477
2478
if (xsltGetSourceNodeFlags(node) & XSLT_SOURCE_NODE_HAS_KEY)
2479
goto keyed_match;
2480
}
2481
if (ret != NULL)
2482
return(ret);
2483
2484
/*
2485
* Cycle on next curstylesheet import.
2486
*/
2487
curstyle = xsltNextImport(curstyle);
2488
}
2489
2490
error:
2491
return(NULL);
2492
}
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
xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
2503
}
2504
2505
/**
2506
* xsltFreeTemplateHashes:
2507
* @style: an XSLT stylesheet
2508
*
2509
* Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2510
*/
2511
void
2512
xsltFreeTemplateHashes(xsltStylesheetPtr style) {
2513
if (style->templatesHash != NULL)
2514
xmlHashFree(style->templatesHash, xsltFreeCompMatchListEntry);
2515
if (style->rootMatch != NULL)
2516
xsltFreeCompMatchList(style->rootMatch);
2517
if (style->keyMatch != NULL)
2518
xsltFreeCompMatchList(style->keyMatch);
2519
if (style->elemMatch != NULL)
2520
xsltFreeCompMatchList(style->elemMatch);
2521
if (style->attrMatch != NULL)
2522
xsltFreeCompMatchList(style->attrMatch);
2523
if (style->parentMatch != NULL)
2524
xsltFreeCompMatchList(style->parentMatch);
2525
if (style->textMatch != NULL)
2526
xsltFreeCompMatchList(style->textMatch);
2527
if (style->piMatch != NULL)
2528
xsltFreeCompMatchList(style->piMatch);
2529
if (style->commentMatch != NULL)
2530
xsltFreeCompMatchList(style->commentMatch);
2531
if (style->namedTemplates != NULL)
2532
xmlHashFree(style->namedTemplates, NULL);
2533
}
2534
2535
2536