Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: third_party/libxslt/libxslt/pattern.c

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

Powered by Google App Engine
This is Rietveld 408576698