VirtualBox

source: vbox/trunk/src/libs/libxslt-1.1.22/libxslt/pattern.c@ 8857

最後變更 在這個檔案從8857是 7296,由 vboxsync 提交於 17 年 前

Added libxslt-1.1.22 sources.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette