2 * pattern.c: Implementation of selectors for nodes
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
7 * http://www.w3.org/TR/1999/REC-xml-19991116
9 * See Copyright for the status of this software.
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
22 * - get rid of the "compile" starting with lowercase
23 * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary
30 #include <libxml/xmlmemory.h>
31 #include <libxml/tree.h>
32 #include <libxml/hash.h>
33 #include <libxml/dict.h>
34 #include <libxml/xmlerror.h>
35 #include <libxml/parserInternals.h>
36 #include <libxml/pattern.h>
38 #ifdef LIBXML_PATTERN_ENABLED
40 /* #define DEBUG_STREAMING */
45 #define ERROR(a, b, c, d)
46 #define ERROR5(a, b, c, d, e)
48 #define XML_STREAM_STEP_DESC 1
49 #define XML_STREAM_STEP_FINAL 2
50 #define XML_STREAM_STEP_ROOT 4
51 #define XML_STREAM_STEP_ATTR 8
52 #define XML_STREAM_STEP_NODE 16
53 #define XML_STREAM_STEP_IN_SET 32
56 * NOTE: Those private flags (XML_STREAM_xxx) are used
57 * in _xmlStreamCtxt->flag. They extend the public
58 * xmlPatternFlags, so be careful not to interfere with the
59 * reserved values for xmlPatternFlags.
61 #define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62 #define XML_STREAM_FROM_ROOT 1<<15
63 #define XML_STREAM_DESC 1<<16
66 * XML_STREAM_ANY_NODE is used for comparison against
67 * xmlElementType enums, to indicate a node of any type.
69 #define XML_STREAM_ANY_NODE 100
71 #define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
75 #define XML_STREAM_XS_IDC(c) ((c)->flags & \
76 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
78 #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
80 #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
82 #define XML_PAT_COPY_NSNAME(c, r, nsname) \
83 if ((c)->comp->dict) \
84 r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
85 else r = xmlStrdup(BAD_CAST nsname);
87 #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
89 typedef struct _xmlStreamStep xmlStreamStep
;
90 typedef xmlStreamStep
*xmlStreamStepPtr
;
91 struct _xmlStreamStep
{
92 int flags
; /* properties of that step */
93 const xmlChar
*name
; /* first string value if NULL accept all */
94 const xmlChar
*ns
; /* second string value */
95 int nodeType
; /* type of node */
98 typedef struct _xmlStreamComp xmlStreamComp
;
99 typedef xmlStreamComp
*xmlStreamCompPtr
;
100 struct _xmlStreamComp
{
101 xmlDict
*dict
; /* the dictionary if any */
102 int nbStep
; /* number of steps in the automata */
103 int maxStep
; /* allocated number of steps */
104 xmlStreamStepPtr steps
; /* the array of steps */
108 struct _xmlStreamCtxt
{
109 struct _xmlStreamCtxt
*next
;/* link to next sub pattern if | */
110 xmlStreamCompPtr comp
; /* the compiled stream */
111 int nbState
; /* number of states in the automata */
112 int maxState
; /* allocated number of states */
113 int level
; /* how deep are we ? */
114 int *states
; /* the array of step indexes */
115 int flags
; /* validation options */
119 static void xmlFreeStreamComp(xmlStreamCompPtr comp
);
138 typedef struct _xmlStepState xmlStepState
;
139 typedef xmlStepState
*xmlStepStatePtr
;
140 struct _xmlStepState
{
145 typedef struct _xmlStepStates xmlStepStates
;
146 typedef xmlStepStates
*xmlStepStatesPtr
;
147 struct _xmlStepStates
{
150 xmlStepStatePtr states
;
153 typedef struct _xmlStepOp xmlStepOp
;
154 typedef xmlStepOp
*xmlStepOpPtr
;
157 const xmlChar
*value
;
158 const xmlChar
*value2
; /* The namespace name */
161 #define PAT_FROM_ROOT (1<<8)
162 #define PAT_FROM_CUR (1<<9)
165 void *data
; /* the associated template */
166 xmlDictPtr dict
; /* the optional dictionary */
167 struct _xmlPattern
*next
; /* next pattern if | is used */
168 const xmlChar
*pattern
; /* the pattern */
169 int flags
; /* flags */
172 xmlStepOpPtr steps
; /* ops for computation */
173 xmlStreamCompPtr stream
; /* the streaming data if any */
176 typedef struct _xmlPatParserContext xmlPatParserContext
;
177 typedef xmlPatParserContext
*xmlPatParserContextPtr
;
178 struct _xmlPatParserContext
{
179 const xmlChar
*cur
; /* the current char being parsed */
180 const xmlChar
*base
; /* the full expression */
181 int error
; /* error code */
182 xmlDictPtr dict
; /* the dictionary if any */
183 xmlPatternPtr comp
; /* the result */
184 xmlNodePtr elem
; /* the current node if any */
185 const xmlChar
**namespaces
; /* the namespaces definitions */
186 int nb_namespaces
; /* the number of namespaces */
189 /************************************************************************
193 ************************************************************************/
198 * Create a new XSLT Pattern
200 * Returns the newly allocated xmlPatternPtr or NULL in case of error
203 xmlNewPattern(void) {
206 cur
= (xmlPatternPtr
) xmlMalloc(sizeof(xmlPattern
));
208 ERROR(NULL
, NULL
, NULL
,
209 "xmlNewPattern : malloc failed\n");
212 memset(cur
, 0, sizeof(xmlPattern
));
214 cur
->steps
= (xmlStepOpPtr
) xmlMalloc(cur
->maxStep
* sizeof(xmlStepOp
));
215 if (cur
->steps
== NULL
) {
217 ERROR(NULL
, NULL
, NULL
,
218 "xmlNewPattern : malloc failed\n");
226 * @comp: an XSLT comp
228 * Free up the memory allocated by @comp
231 xmlFreePattern(xmlPatternPtr comp
) {
232 xmlFreePatternList(comp
);
236 xmlFreePatternInternal(xmlPatternPtr comp
) {
242 if (comp
->stream
!= NULL
)
243 xmlFreeStreamComp(comp
->stream
);
244 if (comp
->pattern
!= NULL
)
245 xmlFree((xmlChar
*)comp
->pattern
);
246 if (comp
->steps
!= NULL
) {
247 if (comp
->dict
== NULL
) {
248 for (i
= 0;i
< comp
->nbStep
;i
++) {
249 op
= &comp
->steps
[i
];
250 if (op
->value
!= NULL
)
251 xmlFree((xmlChar
*) op
->value
);
252 if (op
->value2
!= NULL
)
253 xmlFree((xmlChar
*) op
->value2
);
256 xmlFree(comp
->steps
);
258 if (comp
->dict
!= NULL
)
259 xmlDictFree(comp
->dict
);
261 memset(comp
, -1, sizeof(xmlPattern
));
266 * xmlFreePatternList:
267 * @comp: an XSLT comp list
269 * Free up the memory allocated by all the elements of @comp
272 xmlFreePatternList(xmlPatternPtr comp
) {
275 while (comp
!= NULL
) {
279 xmlFreePatternInternal(cur
);
284 * xmlNewPatParserContext:
285 * @pattern: the pattern context
286 * @dict: the inherited dictionary or NULL
287 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
288 * with [NULL, NULL] or NULL if no namespace is used
290 * Create a new XML pattern parser context
292 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
294 static xmlPatParserContextPtr
295 xmlNewPatParserContext(const xmlChar
*pattern
, xmlDictPtr dict
,
296 const xmlChar
**namespaces
) {
297 xmlPatParserContextPtr cur
;
302 cur
= (xmlPatParserContextPtr
) xmlMalloc(sizeof(xmlPatParserContext
));
304 ERROR(NULL
, NULL
, NULL
,
305 "xmlNewPatParserContext : malloc failed\n");
308 memset(cur
, 0, sizeof(xmlPatParserContext
));
312 if (namespaces
!= NULL
) {
314 for (i
= 0;namespaces
[2 * i
] != NULL
;i
++)
316 cur
->nb_namespaces
= i
;
318 cur
->nb_namespaces
= 0;
320 cur
->namespaces
= namespaces
;
325 * xmlFreePatParserContext:
326 * @ctxt: an XSLT parser context
328 * Free up the memory allocated by @ctxt
331 xmlFreePatParserContext(xmlPatParserContextPtr ctxt
) {
334 memset(ctxt
, -1, sizeof(xmlPatParserContext
));
340 * @comp: the compiled match expression
342 * @value: the first value
343 * @value2: the second value
345 * Add a step to an XSLT Compiled Match
347 * Returns -1 in case of failure, 0 otherwise.
350 xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED
,
352 xmlPatOp op
, xmlChar
* value
, xmlChar
* value2
)
354 if (comp
->nbStep
>= comp
->maxStep
) {
356 temp
= (xmlStepOpPtr
) xmlRealloc(comp
->steps
, comp
->maxStep
* 2 *
359 ERROR(ctxt
, NULL
, NULL
,
360 "xmlPatternAdd: realloc failed\n");
366 comp
->steps
[comp
->nbStep
].op
= op
;
367 comp
->steps
[comp
->nbStep
].value
= value
;
368 comp
->steps
[comp
->nbStep
].value2
= value2
;
375 * xsltSwapTopPattern:
376 * @comp: the compiled match expression
378 * reverse the two top steps.
381 xsltSwapTopPattern(xmlPatternPtr comp
) {
383 int j
= comp
->nbStep
- 1;
386 register const xmlChar
*tmp
;
387 register xmlPatOp op
;
389 tmp
= comp
->steps
[i
].value
;
390 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
391 comp
->steps
[j
].value
= tmp
;
392 tmp
= comp
->steps
[i
].value2
;
393 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
394 comp
->steps
[j
].value2
= tmp
;
395 op
= comp
->steps
[i
].op
;
396 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
397 comp
->steps
[j
].op
= op
;
404 * @comp: the compiled match expression
406 * reverse all the stack of expressions
408 * returns 0 in case of success and -1 in case of error.
411 xmlReversePattern(xmlPatternPtr comp
) {
415 * remove the leading // for //a or .//a
417 if ((comp
->nbStep
> 0) && (comp
->steps
[0].op
== XML_OP_ANCESTOR
)) {
418 for (i
= 0, j
= 1;j
< comp
->nbStep
;i
++,j
++) {
419 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
420 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
421 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
425 if (comp
->nbStep
>= comp
->maxStep
) {
427 temp
= (xmlStepOpPtr
) xmlRealloc(comp
->steps
, comp
->maxStep
* 2 *
430 ERROR(ctxt
, NULL
, NULL
,
431 "xmlReversePattern: realloc failed\n");
438 j
= comp
->nbStep
- 1;
440 register const xmlChar
*tmp
;
441 register xmlPatOp op
;
442 tmp
= comp
->steps
[i
].value
;
443 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
444 comp
->steps
[j
].value
= tmp
;
445 tmp
= comp
->steps
[i
].value2
;
446 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
447 comp
->steps
[j
].value2
= tmp
;
448 op
= comp
->steps
[i
].op
;
449 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
450 comp
->steps
[j
].op
= op
;
454 comp
->steps
[comp
->nbStep
].value
= NULL
;
455 comp
->steps
[comp
->nbStep
].value2
= NULL
;
456 comp
->steps
[comp
->nbStep
++].op
= XML_OP_END
;
460 /************************************************************************
462 * The interpreter for the precompiled patterns *
464 ************************************************************************/
467 xmlPatPushState(xmlStepStates
*states
, int step
, xmlNodePtr node
) {
468 if ((states
->states
== NULL
) || (states
->maxstates
<= 0)) {
469 states
->maxstates
= 4;
470 states
->nbstates
= 0;
471 states
->states
= xmlMalloc(4 * sizeof(xmlStepState
));
473 else if (states
->maxstates
<= states
->nbstates
) {
476 tmp
= (xmlStepStatePtr
) xmlRealloc(states
->states
,
477 2 * states
->maxstates
* sizeof(xmlStepState
));
480 states
->states
= tmp
;
481 states
->maxstates
*= 2;
483 states
->states
[states
->nbstates
].step
= step
;
484 states
->states
[states
->nbstates
++].node
= node
;
486 fprintf(stderr
, "Push: %d, %s\n", step
, node
->name
);
493 * @comp: the precompiled pattern
496 * Test whether the node matches the pattern
498 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
501 xmlPatMatch(xmlPatternPtr comp
, xmlNodePtr node
) {
504 xmlStepStates states
= {0, 0, NULL
}; /* // may require backtrack */
506 if ((comp
== NULL
) || (node
== NULL
)) return(-1);
509 for (;i
< comp
->nbStep
;i
++) {
510 step
= &comp
->steps
[i
];
515 if (node
->type
== XML_NAMESPACE_DECL
)
518 if ((node
->type
== XML_DOCUMENT_NODE
) ||
519 (node
->type
== XML_HTML_DOCUMENT_NODE
))
523 if (node
->type
!= XML_ELEMENT_NODE
)
525 if (step
->value
== NULL
)
527 if (step
->value
[0] != node
->name
[0])
529 if (!xmlStrEqual(step
->value
, node
->name
))
533 if (node
->ns
== NULL
) {
534 if (step
->value2
!= NULL
)
536 } else if (node
->ns
->href
!= NULL
) {
537 if (step
->value2
== NULL
)
539 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
546 if ((node
->type
!= XML_ELEMENT_NODE
) &&
547 (node
->type
!= XML_DOCUMENT_NODE
) &&
548 (node
->type
!= XML_HTML_DOCUMENT_NODE
))
551 lst
= node
->children
;
553 if (step
->value
!= NULL
) {
554 while (lst
!= NULL
) {
555 if ((lst
->type
== XML_ELEMENT_NODE
) &&
556 (step
->value
[0] == lst
->name
[0]) &&
557 (xmlStrEqual(step
->value
, lst
->name
)))
567 if (node
->type
!= XML_ATTRIBUTE_NODE
)
569 if (step
->value
!= NULL
) {
570 if (step
->value
[0] != node
->name
[0])
572 if (!xmlStrEqual(step
->value
, node
->name
))
576 if (node
->ns
== NULL
) {
577 if (step
->value2
!= NULL
)
579 } else if (step
->value2
!= NULL
) {
580 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
585 if ((node
->type
== XML_DOCUMENT_NODE
) ||
586 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
587 (node
->type
== XML_NAMESPACE_DECL
))
592 if (step
->value
== NULL
)
594 if (step
->value
[0] != node
->name
[0])
596 if (!xmlStrEqual(step
->value
, node
->name
))
599 if (node
->ns
== NULL
) {
600 if (step
->value2
!= NULL
)
602 } else if (node
->ns
->href
!= NULL
) {
603 if (step
->value2
== NULL
)
605 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
609 case XML_OP_ANCESTOR
:
610 /* TODO: implement coalescing of ANCESTOR/NODE ops */
611 if (step
->value
== NULL
) {
613 step
= &comp
->steps
[i
];
614 if (step
->op
== XML_OP_ROOT
)
616 if (step
->op
!= XML_OP_ELEM
)
618 if (step
->value
== NULL
)
623 if ((node
->type
== XML_DOCUMENT_NODE
) ||
624 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
625 (node
->type
== XML_NAMESPACE_DECL
))
628 while (node
!= NULL
) {
629 if ((node
->type
== XML_ELEMENT_NODE
) &&
630 (step
->value
[0] == node
->name
[0]) &&
631 (xmlStrEqual(step
->value
, node
->name
))) {
633 if (node
->ns
== NULL
) {
634 if (step
->value2
== NULL
)
636 } else if (node
->ns
->href
!= NULL
) {
637 if ((step
->value2
!= NULL
) &&
638 (xmlStrEqual(step
->value2
, node
->ns
->href
)))
647 * prepare a potential rollback from here
648 * for ancestors of that node.
650 if (step
->op
== XML_OP_ANCESTOR
)
651 xmlPatPushState(&states
, i
, node
);
653 xmlPatPushState(&states
, i
- 1, node
);
656 if (node
->type
!= XML_ELEMENT_NODE
)
658 if (node
->ns
== NULL
) {
659 if (step
->value
!= NULL
)
661 } else if (node
->ns
->href
!= NULL
) {
662 if (step
->value
== NULL
)
664 if (!xmlStrEqual(step
->value
, node
->ns
->href
))
669 if (node
->type
!= XML_ELEMENT_NODE
)
675 if (states
.states
!= NULL
) {
676 /* Free the rollback states */
677 xmlFree(states
.states
);
681 /* got an error try to rollback */
682 if (states
.states
== NULL
)
684 if (states
.nbstates
<= 0) {
685 xmlFree(states
.states
);
689 i
= states
.states
[states
.nbstates
].step
;
690 node
= states
.states
[states
.nbstates
].node
;
692 fprintf(stderr
, "Pop: %d, %s\n", i
, node
->name
);
697 /************************************************************************
699 * Dedicated parser for templates *
701 ************************************************************************/
704 xmlGenericError(xmlGenericErrorContext, \
705 "Unimplemented block at %s:%d\n", \
707 #define CUR (*ctxt->cur)
708 #define SKIP(val) ctxt->cur += (val)
709 #define NXT(val) ctxt->cur[(val)]
710 #define PEEKPREV(val) ctxt->cur[-(val)]
711 #define CUR_PTR ctxt->cur
713 #define SKIP_BLANKS \
714 while (IS_BLANK_CH(CUR)) NEXT
716 #define CURRENT (*ctxt->cur)
717 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
720 #define PUSH(op, val, val2) \
721 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
723 #define XSLT_ERROR(X) \
724 { xsltError(ctxt, __FILE__, __LINE__, X); \
725 ctxt->error = (X); return; }
727 #define XSLT_ERROR0(X) \
728 { xsltError(ctxt, __FILE__, __LINE__, X); \
729 ctxt->error = (X); return(0); }
734 * @ctxt: the XPath Parser context
736 * Parse an XPath Literal:
738 * [29] Literal ::= '"' [^"]* '"'
741 * Returns the Literal parsed or NULL
745 xmlPatScanLiteral(xmlPatParserContextPtr ctxt
) {
746 const xmlChar
*q
, *cur
;
754 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
755 while ((IS_CHAR(val
)) && (val
!= '"')) {
757 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
764 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
766 ret
= xmlStrndup(q
, cur
- q
);
770 } else if (CUR
== '\'') {
773 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
774 while ((IS_CHAR(val
)) && (val
!= '\'')) {
776 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
783 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
785 ret
= xmlStrndup(q
, cur
- q
);
790 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
800 * @ctxt: the XPath Parser context
802 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
803 * CombiningChar | Extender
805 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
807 * [6] Names ::= Name (S Name)*
809 * Returns the Name parsed or NULL
813 xmlPatScanName(xmlPatParserContextPtr ctxt
) {
814 const xmlChar
*q
, *cur
;
821 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
822 if (!IS_LETTER(val
) && (val
!= '_') && (val
!= ':'))
825 while ((IS_LETTER(val
)) || (IS_DIGIT(val
)) ||
826 (val
== '.') || (val
== '-') ||
828 (IS_COMBINING(val
)) ||
829 (IS_EXTENDER(val
))) {
831 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
834 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
836 ret
= xmlStrndup(q
, cur
- q
);
843 * @ctxt: the XPath Parser context
845 * Parses a non qualified name
847 * Returns the Name parsed or NULL
851 xmlPatScanNCName(xmlPatParserContextPtr ctxt
) {
852 const xmlChar
*q
, *cur
;
859 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
860 if (!IS_LETTER(val
) && (val
!= '_'))
863 while ((IS_LETTER(val
)) || (IS_DIGIT(val
)) ||
864 (val
== '.') || (val
== '-') ||
866 (IS_COMBINING(val
)) ||
867 (IS_EXTENDER(val
))) {
869 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
872 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
874 ret
= xmlStrndup(q
, cur
- q
);
882 * @ctxt: the XPath Parser context
883 * @prefix: the place to store the prefix
885 * Parse a qualified name
887 * Returns the Name parsed or NULL
891 xmlPatScanQName(xmlPatParserContextPtr ctxt
, xmlChar
**prefix
) {
895 ret
= xmlPatScanNCName(ctxt
);
899 ret
= xmlPatScanNCName(ctxt
);
906 * xmlCompileAttributeTest:
907 * @ctxt: the compilation context
909 * Compile an attribute test.
912 xmlCompileAttributeTest(xmlPatParserContextPtr ctxt
) {
913 xmlChar
*token
= NULL
;
914 xmlChar
*name
= NULL
;
918 name
= xmlPatScanNCName(ctxt
);
921 PUSH(XML_OP_ATTR
, NULL
, NULL
);
924 ERROR(NULL
, NULL
, NULL
,
925 "xmlCompileAttributeTest : Name expected\n");
932 xmlChar
*prefix
= name
;
936 if (IS_BLANK_CH(CUR
)) {
937 ERROR5(NULL
, NULL
, NULL
, "Invalid QName.\n", NULL
);
938 XML_PAT_FREE_STRING(ctxt
, prefix
);
943 * This is a namespace match
945 token
= xmlPatScanName(ctxt
);
946 if ((prefix
[0] == 'x') &&
947 (prefix
[1] == 'm') &&
948 (prefix
[2] == 'l') &&
951 XML_PAT_COPY_NSNAME(ctxt
, URL
, XML_XML_NAMESPACE
);
953 for (i
= 0;i
< ctxt
->nb_namespaces
;i
++) {
954 if (xmlStrEqual(ctxt
->namespaces
[2 * i
+ 1], prefix
)) {
955 XML_PAT_COPY_NSNAME(ctxt
, URL
, ctxt
->namespaces
[2 * i
])
959 if (i
>= ctxt
->nb_namespaces
) {
960 ERROR5(NULL
, NULL
, NULL
,
961 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
963 XML_PAT_FREE_STRING(ctxt
, prefix
);
968 XML_PAT_FREE_STRING(ctxt
, prefix
);
972 PUSH(XML_OP_ATTR
, NULL
, URL
);
974 ERROR(NULL
, NULL
, NULL
,
975 "xmlCompileAttributeTest : Name expected\n");
980 PUSH(XML_OP_ATTR
, token
, URL
);
983 PUSH(XML_OP_ATTR
, name
, NULL
);
988 XML_PAT_FREE_STRING(ctxt
, URL
)
990 XML_PAT_FREE_STRING(ctxt
, token
);
994 * xmlCompileStepPattern:
995 * @ctxt: the compilation context
997 * Compile the Step Pattern and generates a precompiled
998 * form suitable for fast matching.
1000 * [3] Step ::= '.' | NameTest
1001 * [4] NameTest ::= QName | '*' | NCName ':' '*'
1005 xmlCompileStepPattern(xmlPatParserContextPtr ctxt
) {
1006 xmlChar
*token
= NULL
;
1007 xmlChar
*name
= NULL
;
1008 xmlChar
*URL
= NULL
;
1017 PUSH(XML_OP_ELEM
, NULL
, NULL
);
1024 if (XML_STREAM_XS_IDC_SEL(ctxt
->comp
)) {
1025 ERROR5(NULL
, NULL
, NULL
,
1026 "Unexpected attribute axis in '%s'.\n", ctxt
->base
);
1031 xmlCompileAttributeTest(ctxt
);
1032 if (ctxt
->error
!= 0)
1036 name
= xmlPatScanNCName(ctxt
);
1040 PUSH(XML_OP_ALL
, NULL
, NULL
);
1043 ERROR(NULL
, NULL
, NULL
,
1044 "xmlCompileStepPattern : Name expected\n");
1049 if (IS_BLANK_CH(CUR
)) {
1056 xmlChar
*prefix
= name
;
1059 if (hasBlanks
|| IS_BLANK_CH(CUR
)) {
1060 ERROR5(NULL
, NULL
, NULL
, "Invalid QName.\n", NULL
);
1065 * This is a namespace match
1067 token
= xmlPatScanName(ctxt
);
1068 if ((prefix
[0] == 'x') &&
1069 (prefix
[1] == 'm') &&
1070 (prefix
[2] == 'l') &&
1073 XML_PAT_COPY_NSNAME(ctxt
, URL
, XML_XML_NAMESPACE
)
1075 for (i
= 0;i
< ctxt
->nb_namespaces
;i
++) {
1076 if (xmlStrEqual(ctxt
->namespaces
[2 * i
+ 1], prefix
)) {
1077 XML_PAT_COPY_NSNAME(ctxt
, URL
, ctxt
->namespaces
[2 * i
])
1081 if (i
>= ctxt
->nb_namespaces
) {
1082 ERROR5(NULL
, NULL
, NULL
,
1083 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1089 XML_PAT_FREE_STRING(ctxt
, prefix
);
1091 if (token
== NULL
) {
1094 PUSH(XML_OP_NS
, URL
, NULL
);
1096 ERROR(NULL
, NULL
, NULL
,
1097 "xmlCompileStepPattern : Name expected\n");
1102 PUSH(XML_OP_ELEM
, token
, URL
);
1106 if (xmlStrEqual(name
, (const xmlChar
*) "child")) {
1107 XML_PAT_FREE_STRING(ctxt
, name
);
1108 name
= xmlPatScanName(ctxt
);
1112 PUSH(XML_OP_ALL
, NULL
, NULL
);
1115 ERROR(NULL
, NULL
, NULL
,
1116 "xmlCompileStepPattern : QName expected\n");
1122 xmlChar
*prefix
= name
;
1126 if (IS_BLANK_CH(CUR
)) {
1127 ERROR5(NULL
, NULL
, NULL
, "Invalid QName.\n", NULL
);
1132 * This is a namespace match
1134 token
= xmlPatScanName(ctxt
);
1135 if ((prefix
[0] == 'x') &&
1136 (prefix
[1] == 'm') &&
1137 (prefix
[2] == 'l') &&
1140 XML_PAT_COPY_NSNAME(ctxt
, URL
, XML_XML_NAMESPACE
)
1142 for (i
= 0;i
< ctxt
->nb_namespaces
;i
++) {
1143 if (xmlStrEqual(ctxt
->namespaces
[2 * i
+ 1], prefix
)) {
1144 XML_PAT_COPY_NSNAME(ctxt
, URL
, ctxt
->namespaces
[2 * i
])
1148 if (i
>= ctxt
->nb_namespaces
) {
1149 ERROR5(NULL
, NULL
, NULL
,
1150 "xmlCompileStepPattern : no namespace bound "
1151 "to prefix %s\n", prefix
);
1156 XML_PAT_FREE_STRING(ctxt
, prefix
);
1158 if (token
== NULL
) {
1161 PUSH(XML_OP_NS
, URL
, NULL
);
1163 ERROR(NULL
, NULL
, NULL
,
1164 "xmlCompileStepPattern : Name expected\n");
1169 PUSH(XML_OP_CHILD
, token
, URL
);
1172 PUSH(XML_OP_CHILD
, name
, NULL
);
1174 } else if (xmlStrEqual(name
, (const xmlChar
*) "attribute")) {
1175 XML_PAT_FREE_STRING(ctxt
, name
)
1177 if (XML_STREAM_XS_IDC_SEL(ctxt
->comp
)) {
1178 ERROR5(NULL
, NULL
, NULL
,
1179 "Unexpected attribute axis in '%s'.\n", ctxt
->base
);
1183 xmlCompileAttributeTest(ctxt
);
1184 if (ctxt
->error
!= 0)
1188 ERROR5(NULL
, NULL
, NULL
,
1189 "The 'element' or 'attribute' axis is expected.\n", NULL
);
1194 } else if (CUR
== '*') {
1200 PUSH(XML_OP_ALL
, token
, NULL
);
1202 PUSH(XML_OP_ELEM
, name
, NULL
);
1207 XML_PAT_FREE_STRING(ctxt
, URL
)
1209 XML_PAT_FREE_STRING(ctxt
, token
)
1211 XML_PAT_FREE_STRING(ctxt
, name
)
1215 * xmlCompilePathPattern:
1216 * @ctxt: the compilation context
1218 * Compile the Path Pattern and generates a precompiled
1219 * form suitable for fast matching.
1221 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1224 xmlCompilePathPattern(xmlPatParserContextPtr ctxt
) {
1227 ctxt
->comp
->flags
|= PAT_FROM_ROOT
;
1228 } else if ((CUR
== '.') || (ctxt
->comp
->flags
& XML_PATTERN_NOTPATTERN
)) {
1229 ctxt
->comp
->flags
|= PAT_FROM_CUR
;
1232 if ((CUR
== '/') && (NXT(1) == '/')) {
1233 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1236 } else if ((CUR
== '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1237 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1241 /* Check for incompleteness. */
1244 ERROR5(NULL
, NULL
, NULL
,
1245 "Incomplete expression '%s'.\n", ctxt
->base
);
1252 xmlCompileAttributeTest(ctxt
);
1254 /* TODO: check for incompleteness */
1256 xmlCompileStepPattern(ctxt
);
1257 if (ctxt
->error
!= 0)
1262 PUSH(XML_OP_ROOT
, NULL
, NULL
);
1264 /* Check for incompleteness. */
1267 ERROR5(NULL
, NULL
, NULL
,
1268 "Incomplete expression '%s'.\n", ctxt
->base
);
1273 xmlCompileStepPattern(ctxt
);
1274 if (ctxt
->error
!= 0)
1277 while (CUR
== '/') {
1278 if (NXT(1) == '/') {
1279 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1283 xmlCompileStepPattern(ctxt
);
1284 if (ctxt
->error
!= 0)
1287 PUSH(XML_OP_PARENT
, NULL
, NULL
);
1291 ERROR5(NULL
, NULL
, NULL
,
1292 "Incomplete expression '%s'.\n", ctxt
->base
);
1296 xmlCompileStepPattern(ctxt
);
1297 if (ctxt
->error
!= 0)
1303 ERROR5(NULL
, NULL
, NULL
,
1304 "Failed to compile pattern %s\n", ctxt
->base
);
1312 * xmlCompileIDCXPathPath:
1313 * @ctxt: the compilation context
1315 * Compile the Path Pattern and generates a precompiled
1316 * form suitable for fast matching.
1318 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1321 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt
) {
1324 ERROR5(NULL
, NULL
, NULL
,
1325 "Unexpected selection of the document root in '%s'.\n",
1329 ctxt
->comp
->flags
|= PAT_FROM_CUR
;
1332 /* "." - "self::node()" */
1337 * Selection of the context node.
1339 PUSH(XML_OP_ELEM
, NULL
, NULL
);
1343 /* TODO: A more meaningful error message. */
1344 ERROR5(NULL
, NULL
, NULL
,
1345 "Unexpected token after '.' in '%s'.\n", ctxt
->base
);
1348 /* "./" - "self::node()/" */
1352 if (IS_BLANK_CH(PEEKPREV(1))) {
1356 ERROR5(NULL
, NULL
, NULL
,
1357 "Unexpected '/' token in '%s'.\n", ctxt
->base
);
1360 /* ".//" - "self:node()/descendant-or-self::node()/" */
1361 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1366 goto error_unfinished
;
1372 xmlCompileStepPattern(ctxt
);
1373 if (ctxt
->error
!= 0)
1378 PUSH(XML_OP_PARENT
, NULL
, NULL
);
1383 * Disallow subsequent '//'.
1385 ERROR5(NULL
, NULL
, NULL
,
1386 "Unexpected subsequent '//' in '%s'.\n",
1391 goto error_unfinished
;
1396 ERROR5(NULL
, NULL
, NULL
,
1397 "Failed to compile expression '%s'.\n", ctxt
->base
);
1407 ERROR5(NULL
, NULL
, NULL
,
1408 "Unfinished expression '%s'.\n", ctxt
->base
);
1412 /************************************************************************
1414 * The streaming code *
1416 ************************************************************************/
1418 #ifdef DEBUG_STREAMING
1420 xmlDebugStreamComp(xmlStreamCompPtr stream
) {
1423 if (stream
== NULL
) {
1424 printf("Stream: NULL\n");
1427 printf("Stream: %d steps\n", stream
->nbStep
);
1428 for (i
= 0;i
< stream
->nbStep
;i
++) {
1429 if (stream
->steps
[i
].ns
!= NULL
) {
1430 printf("{%s}", stream
->steps
[i
].ns
);
1432 if (stream
->steps
[i
].name
== NULL
) {
1435 printf("%s ", stream
->steps
[i
].name
);
1437 if (stream
->steps
[i
].flags
& XML_STREAM_STEP_ROOT
)
1439 if (stream
->steps
[i
].flags
& XML_STREAM_STEP_DESC
)
1441 if (stream
->steps
[i
].flags
& XML_STREAM_STEP_FINAL
)
1447 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt
, int match
) {
1451 printf("Stream: NULL\n");
1454 printf("Stream: level %d, %d states: ", ctxt
->level
, ctxt
->nbState
);
1456 printf("matches\n");
1459 for (i
= 0;i
< ctxt
->nbState
;i
++) {
1460 if (ctxt
->states
[2 * i
] < 0)
1461 printf(" %d: free\n", i
);
1463 printf(" %d: step %d, level %d", i
, ctxt
->states
[2 * i
],
1464 ctxt
->states
[(2 * i
) + 1]);
1465 if (ctxt
->comp
->steps
[ctxt
->states
[2 * i
]].flags
&
1466 XML_STREAM_STEP_DESC
)
1476 * @size: the number of expected steps
1478 * build a new compiled pattern for streaming
1480 * Returns the new structure or NULL in case of error.
1482 static xmlStreamCompPtr
1483 xmlNewStreamComp(int size
) {
1484 xmlStreamCompPtr cur
;
1489 cur
= (xmlStreamCompPtr
) xmlMalloc(sizeof(xmlStreamComp
));
1491 ERROR(NULL
, NULL
, NULL
,
1492 "xmlNewStreamComp: malloc failed\n");
1495 memset(cur
, 0, sizeof(xmlStreamComp
));
1496 cur
->steps
= (xmlStreamStepPtr
) xmlMalloc(size
* sizeof(xmlStreamStep
));
1497 if (cur
->steps
== NULL
) {
1499 ERROR(NULL
, NULL
, NULL
,
1500 "xmlNewStreamComp: malloc failed\n");
1504 cur
->maxStep
= size
;
1509 * xmlFreeStreamComp:
1510 * @comp: the compiled pattern for streaming
1512 * Free the compiled pattern for streaming
1515 xmlFreeStreamComp(xmlStreamCompPtr comp
) {
1517 if (comp
->steps
!= NULL
)
1518 xmlFree(comp
->steps
);
1519 if (comp
->dict
!= NULL
)
1520 xmlDictFree(comp
->dict
);
1526 * xmlStreamCompAddStep:
1527 * @comp: the compiled pattern for streaming
1528 * @name: the first string, the name, or NULL for *
1529 * @ns: the second step, the namespace name
1530 * @flags: the flags for that step
1532 * Add a new step to the compiled pattern
1534 * Returns -1 in case of error or the step index if successful
1537 xmlStreamCompAddStep(xmlStreamCompPtr comp
, const xmlChar
*name
,
1538 const xmlChar
*ns
, int nodeType
, int flags
) {
1539 xmlStreamStepPtr cur
;
1541 if (comp
->nbStep
>= comp
->maxStep
) {
1542 cur
= (xmlStreamStepPtr
) xmlRealloc(comp
->steps
,
1543 comp
->maxStep
* 2 * sizeof(xmlStreamStep
));
1545 ERROR(NULL
, NULL
, NULL
,
1546 "xmlNewStreamComp: malloc failed\n");
1552 cur
= &comp
->steps
[comp
->nbStep
++];
1556 cur
->nodeType
= nodeType
;
1557 return(comp
->nbStep
- 1);
1562 * @comp: the precompiled pattern
1564 * Tries to stream compile a pattern
1566 * Returns -1 in case of failure and 0 in case of success.
1569 xmlStreamCompile(xmlPatternPtr comp
) {
1570 xmlStreamCompPtr stream
;
1571 int i
, s
= 0, root
= 0, flags
= 0, prevs
= -1;
1574 if ((comp
== NULL
) || (comp
->steps
== NULL
))
1577 * special case for .
1579 if ((comp
->nbStep
== 1) &&
1580 (comp
->steps
[0].op
== XML_OP_ELEM
) &&
1581 (comp
->steps
[0].value
== NULL
) &&
1582 (comp
->steps
[0].value2
== NULL
)) {
1583 stream
= xmlNewStreamComp(0);
1586 /* Note that the stream will have no steps in this case. */
1587 stream
->flags
|= XML_STREAM_FINAL_IS_ANY_NODE
;
1588 comp
->stream
= stream
;
1592 stream
= xmlNewStreamComp((comp
->nbStep
/ 2) + 1);
1595 if (comp
->dict
!= NULL
) {
1596 stream
->dict
= comp
->dict
;
1597 xmlDictReference(stream
->dict
);
1601 if (comp
->flags
& PAT_FROM_ROOT
)
1602 stream
->flags
|= XML_STREAM_FROM_ROOT
;
1604 for (;i
< comp
->nbStep
;i
++) {
1605 step
= comp
->steps
[i
];
1615 s
= xmlStreamCompAddStep(stream
, NULL
, step
.value
,
1616 XML_ELEMENT_NODE
, flags
);
1623 flags
|= XML_STREAM_STEP_ATTR
;
1625 s
= xmlStreamCompAddStep(stream
,
1626 step
.value
, step
.value2
, XML_ATTRIBUTE_NODE
, flags
);
1632 if ((step
.value
== NULL
) && (step
.value2
== NULL
)) {
1634 * We have a "." or "self::node()" here.
1635 * Eliminate redundant self::node() tests like in "/./."
1637 * The only case we won't eliminate is "//.", i.e. if
1638 * self::node() is the last node test and we had
1639 * continuation somewhere beforehand.
1641 if ((comp
->nbStep
== i
+ 1) &&
1642 (flags
& XML_STREAM_STEP_DESC
)) {
1644 * Mark the special case where the expression resolves
1645 * to any type of node.
1647 if (comp
->nbStep
== i
+ 1) {
1648 stream
->flags
|= XML_STREAM_FINAL_IS_ANY_NODE
;
1650 flags
|= XML_STREAM_STEP_NODE
;
1651 s
= xmlStreamCompAddStep(stream
, NULL
, NULL
,
1652 XML_STREAM_ANY_NODE
, flags
);
1657 * If there was a previous step, mark it to be added to
1658 * the result node-set; this is needed since only
1659 * the last step will be marked as "final" and only
1660 * "final" nodes are added to the resulting set.
1663 stream
->steps
[prevs
].flags
|= XML_STREAM_STEP_IN_SET
;
1669 /* Just skip this one. */
1673 /* An element node. */
1674 s
= xmlStreamCompAddStep(stream
, step
.value
, step
.value2
,
1675 XML_ELEMENT_NODE
, flags
);
1682 /* An element node child. */
1683 s
= xmlStreamCompAddStep(stream
, step
.value
, step
.value2
,
1684 XML_ELEMENT_NODE
, flags
);
1691 s
= xmlStreamCompAddStep(stream
, NULL
, NULL
,
1692 XML_ELEMENT_NODE
, flags
);
1700 case XML_OP_ANCESTOR
:
1701 /* Skip redundant continuations. */
1702 if (flags
& XML_STREAM_STEP_DESC
)
1704 flags
|= XML_STREAM_STEP_DESC
;
1706 * Mark the expression as having "//".
1708 if ((stream
->flags
& XML_STREAM_DESC
) == 0)
1709 stream
->flags
|= XML_STREAM_DESC
;
1713 if ((! root
) && (comp
->flags
& XML_PATTERN_NOTPATTERN
) == 0) {
1715 * If this should behave like a real pattern, we will mark
1716 * the first step as having "//", to be reentrant on every
1719 if ((stream
->flags
& XML_STREAM_DESC
) == 0)
1720 stream
->flags
|= XML_STREAM_DESC
;
1722 if (stream
->nbStep
> 0) {
1723 if ((stream
->steps
[0].flags
& XML_STREAM_STEP_DESC
) == 0)
1724 stream
->steps
[0].flags
|= XML_STREAM_STEP_DESC
;
1727 if (stream
->nbStep
<= s
)
1729 stream
->steps
[s
].flags
|= XML_STREAM_STEP_FINAL
;
1731 stream
->steps
[0].flags
|= XML_STREAM_STEP_ROOT
;
1732 #ifdef DEBUG_STREAMING
1733 xmlDebugStreamComp(stream
);
1735 comp
->stream
= stream
;
1738 xmlFreeStreamComp(stream
);
1744 * @size: the number of expected states
1746 * build a new stream context
1748 * Returns the new structure or NULL in case of error.
1750 static xmlStreamCtxtPtr
1751 xmlNewStreamCtxt(xmlStreamCompPtr stream
) {
1752 xmlStreamCtxtPtr cur
;
1754 cur
= (xmlStreamCtxtPtr
) xmlMalloc(sizeof(xmlStreamCtxt
));
1756 ERROR(NULL
, NULL
, NULL
,
1757 "xmlNewStreamCtxt: malloc failed\n");
1760 memset(cur
, 0, sizeof(xmlStreamCtxt
));
1761 cur
->states
= (int *) xmlMalloc(4 * 2 * sizeof(int));
1762 if (cur
->states
== NULL
) {
1764 ERROR(NULL
, NULL
, NULL
,
1765 "xmlNewStreamCtxt: malloc failed\n");
1772 cur
->blockLevel
= -1;
1777 * xmlFreeStreamCtxt:
1778 * @stream: the stream context
1780 * Free the stream context
1783 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream
) {
1784 xmlStreamCtxtPtr next
;
1786 while (stream
!= NULL
) {
1787 next
= stream
->next
;
1788 if (stream
->states
!= NULL
)
1789 xmlFree(stream
->states
);
1796 * xmlStreamCtxtAddState:
1797 * @comp: the stream context
1798 * @idx: the step index for that streaming state
1800 * Add a new state to the stream context
1802 * Returns -1 in case of error or the state index if successful
1805 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp
, int idx
, int level
) {
1807 for (i
= 0;i
< comp
->nbState
;i
++) {
1808 if (comp
->states
[2 * i
] < 0) {
1809 comp
->states
[2 * i
] = idx
;
1810 comp
->states
[2 * i
+ 1] = level
;
1814 if (comp
->nbState
>= comp
->maxState
) {
1817 cur
= (int *) xmlRealloc(comp
->states
,
1818 comp
->maxState
* 4 * sizeof(int));
1820 ERROR(NULL
, NULL
, NULL
,
1821 "xmlNewStreamCtxt: malloc failed\n");
1825 comp
->maxState
*= 2;
1827 comp
->states
[2 * comp
->nbState
] = idx
;
1828 comp
->states
[2 * comp
->nbState
++ + 1] = level
;
1829 return(comp
->nbState
- 1);
1833 * xmlStreamPushInternal:
1834 * @stream: the stream context
1835 * @name: the current name
1836 * @ns: the namespace name
1837 * @nodeType: the type of the node
1839 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1840 * indicated a dictionary, then strings for name and ns will be expected
1841 * to come from the dictionary.
1842 * Both @name and @ns being NULL means the / i.e. the root of the document.
1843 * This can also act as a reset.
1845 * Returns: -1 in case of error, 1 if the current state in the stream is a
1846 * match and 0 otherwise.
1849 xmlStreamPushInternal(xmlStreamCtxtPtr stream
,
1850 const xmlChar
*name
, const xmlChar
*ns
,
1852 int ret
= 0, err
= 0, final
= 0, tmp
, i
, m
, match
, stepNr
, desc
;
1853 xmlStreamCompPtr comp
;
1855 #ifdef DEBUG_STREAMING
1856 xmlStreamCtxtPtr orig
= stream
;
1859 if ((stream
== NULL
) || (stream
->nbState
< 0))
1862 while (stream
!= NULL
) {
1863 comp
= stream
->comp
;
1865 if ((nodeType
== XML_ELEMENT_NODE
) &&
1866 (name
== NULL
) && (ns
== NULL
)) {
1867 /* We have a document node here (or a reset). */
1868 stream
->nbState
= 0;
1870 stream
->blockLevel
= -1;
1871 if (comp
->flags
& XML_STREAM_FROM_ROOT
) {
1872 if (comp
->nbStep
== 0) {
1873 /* TODO: We have a "/." here? */
1876 if ((comp
->nbStep
== 1) &&
1877 (comp
->steps
[0].nodeType
== XML_STREAM_ANY_NODE
) &&
1878 (comp
->steps
[0].flags
& XML_STREAM_STEP_DESC
))
1881 * In the case of "//." the document node will match
1885 } else if (comp
->steps
[0].flags
& XML_STREAM_STEP_ROOT
) {
1886 /* TODO: Do we need this ? */
1887 tmp
= xmlStreamCtxtAddState(stream
, 0, 0);
1893 stream
= stream
->next
;
1894 continue; /* while */
1898 * Fast check for ".".
1900 if (comp
->nbStep
== 0) {
1902 * / and . are handled at the XPath node set creation
1903 * level by checking min depth
1905 if (stream
->flags
& XML_PATTERN_XPATH
) {
1906 stream
= stream
->next
;
1907 continue; /* while */
1910 * For non-pattern like evaluation like XML Schema IDCs
1911 * or traditional XPath expressions, this will match if
1912 * we are at the first level only, otherwise on every level.
1914 if ((nodeType
!= XML_ATTRIBUTE_NODE
) &&
1915 (((stream
->flags
& XML_PATTERN_NOTPATTERN
) == 0) ||
1916 (stream
->level
== 0))) {
1922 if (stream
->blockLevel
!= -1) {
1924 * Skip blocked expressions.
1930 if ((nodeType
!= XML_ELEMENT_NODE
) &&
1931 (nodeType
!= XML_ATTRIBUTE_NODE
) &&
1932 ((comp
->flags
& XML_STREAM_FINAL_IS_ANY_NODE
) == 0)) {
1934 * No need to process nodes of other types if we don't
1935 * resolve to those types.
1936 * TODO: Do we need to block the context here?
1943 * Check evolution of existing states
1946 m
= stream
->nbState
;
1948 if ((comp
->flags
& XML_STREAM_DESC
) == 0) {
1950 * If there is no "//", then only the last
1951 * added state is of interest.
1953 stepNr
= stream
->states
[2 * (stream
->nbState
-1)];
1955 * TODO: Security check, should not happen, remove it.
1957 if (stream
->states
[(2 * (stream
->nbState
-1)) + 1] <
1966 * If there are "//", then we need to process every "//"
1967 * occurring in the states, plus any other state for this
1970 stepNr
= stream
->states
[2 * i
];
1972 /* TODO: should not happen anymore: dead states */
1976 tmp
= stream
->states
[(2 * i
) + 1];
1978 /* skip new states just added */
1979 if (tmp
> stream
->level
)
1982 /* skip states at ancestor levels, except if "//" */
1983 desc
= comp
->steps
[stepNr
].flags
& XML_STREAM_STEP_DESC
;
1984 if ((tmp
< stream
->level
) && (!desc
))
1988 * Check for correct node-type.
1990 step
= comp
->steps
[stepNr
];
1991 if (step
.nodeType
!= nodeType
) {
1992 if (step
.nodeType
== XML_ATTRIBUTE_NODE
) {
1994 * Block this expression for deeper evaluation.
1996 if ((comp
->flags
& XML_STREAM_DESC
) == 0)
1997 stream
->blockLevel
= stream
->level
+1;
1999 } else if (step
.nodeType
!= XML_STREAM_ANY_NODE
)
2003 * Compare local/namespace-name.
2006 if (step
.nodeType
== XML_STREAM_ANY_NODE
) {
2008 } else if (step
.name
== NULL
) {
2009 if (step
.ns
== NULL
) {
2011 * This lets through all elements/attributes.
2014 } else if (ns
!= NULL
)
2015 match
= xmlStrEqual(step
.ns
, ns
);
2016 } else if (((step
.ns
!= NULL
) == (ns
!= NULL
)) &&
2018 (step
.name
[0] == name
[0]) &&
2019 xmlStrEqual(step
.name
, name
) &&
2020 ((step
.ns
== ns
) || xmlStrEqual(step
.ns
, ns
)))
2026 * TODO: Pointer comparison won't work, since not guaranteed that the given
2027 * values are in the same dict; especially if it's the namespace name,
2028 * normally coming from ns->href. We need a namespace dict mechanism !
2030 } else if (comp
->dict
) {
2031 if (step
.name
== NULL
) {
2032 if (step
.ns
== NULL
)
2035 match
= (step
.ns
== ns
);
2037 match
= ((step
.name
== name
) && (step
.ns
== ns
));
2039 #endif /* if 0 ------------------------------------------------------- */
2041 final
= step
.flags
& XML_STREAM_STEP_FINAL
;
2045 xmlStreamCtxtAddState(stream
, stepNr
+ 1,
2048 if ((ret
!= 1) && (step
.flags
& XML_STREAM_STEP_IN_SET
)) {
2050 * Check if we have a special case like "foo/bar//.", where
2051 * "foo" is selected as well.
2056 if (((comp
->flags
& XML_STREAM_DESC
) == 0) &&
2057 ((! match
) || final
)) {
2059 * Mark this expression as blocked for any evaluation at
2060 * deeper levels. Note that this includes "/foo"
2061 * expressions if the *pattern* behaviour is used.
2063 stream
->blockLevel
= stream
->level
+1;
2072 * Re/enter the expression.
2073 * Don't reenter if it's an absolute expression like "/foo",
2076 step
= comp
->steps
[0];
2077 if (step
.flags
& XML_STREAM_STEP_ROOT
)
2080 desc
= step
.flags
& XML_STREAM_STEP_DESC
;
2081 if (stream
->flags
& XML_PATTERN_NOTPATTERN
) {
2083 * Re/enter the expression if it is a "descendant" one,
2084 * or if we are at the 1st level of evaluation.
2087 if (stream
->level
== 1) {
2088 if (XML_STREAM_XS_IDC(stream
)) {
2090 * XS-IDC: The missing "self::node()" will always
2091 * match the first given node.
2098 * A "//" is always reentrant.
2104 * XS-IDC: Process the 2nd level, since the missing
2105 * "self::node()" is responsible for the 2nd level being
2106 * the real start level.
2108 if ((stream
->level
== 2) && XML_STREAM_XS_IDC(stream
))
2116 * Check expected node-type.
2118 if (step
.nodeType
!= nodeType
) {
2119 if (nodeType
== XML_ATTRIBUTE_NODE
)
2121 else if (step
.nodeType
!= XML_STREAM_ANY_NODE
)
2125 * Compare local/namespace-name.
2128 if (step
.nodeType
== XML_STREAM_ANY_NODE
) {
2130 } else if (step
.name
== NULL
) {
2131 if (step
.ns
== NULL
) {
2133 * This lets through all elements/attributes.
2136 } else if (ns
!= NULL
)
2137 match
= xmlStrEqual(step
.ns
, ns
);
2138 } else if (((step
.ns
!= NULL
) == (ns
!= NULL
)) &&
2140 (step
.name
[0] == name
[0]) &&
2141 xmlStrEqual(step
.name
, name
) &&
2142 ((step
.ns
== ns
) || xmlStrEqual(step
.ns
, ns
)))
2146 final
= step
.flags
& XML_STREAM_STEP_FINAL
;
2151 xmlStreamCtxtAddState(stream
, 1, stream
->level
);
2152 if ((ret
!= 1) && (step
.flags
& XML_STREAM_STEP_IN_SET
)) {
2154 * Check if we have a special case like "foo//.", where
2155 * "foo" is selected as well.
2160 if (((comp
->flags
& XML_STREAM_DESC
) == 0) &&
2161 ((! match
) || final
)) {
2163 * Mark this expression as blocked for any evaluation at
2166 stream
->blockLevel
= stream
->level
;
2170 stream
= stream
->next
;
2171 } /* while stream != NULL */
2175 #ifdef DEBUG_STREAMING
2176 xmlDebugStreamCtxt(orig
, ret
);
2183 * @stream: the stream context
2184 * @name: the current name
2185 * @ns: the namespace name
2187 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2188 * indicated a dictionary, then strings for name and ns will be expected
2189 * to come from the dictionary.
2190 * Both @name and @ns being NULL means the / i.e. the root of the document.
2191 * This can also act as a reset.
2192 * Otherwise the function will act as if it has been given an element-node.
2194 * Returns: -1 in case of error, 1 if the current state in the stream is a
2195 * match and 0 otherwise.
2198 xmlStreamPush(xmlStreamCtxtPtr stream
,
2199 const xmlChar
*name
, const xmlChar
*ns
) {
2200 return (xmlStreamPushInternal(stream
, name
, ns
, XML_ELEMENT_NODE
));
2204 * xmlStreamPushNode:
2205 * @stream: the stream context
2206 * @name: the current name
2207 * @ns: the namespace name
2208 * @nodeType: the type of the node being pushed
2210 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2211 * indicated a dictionary, then strings for name and ns will be expected
2212 * to come from the dictionary.
2213 * Both @name and @ns being NULL means the / i.e. the root of the document.
2214 * This can also act as a reset.
2215 * Different from xmlStreamPush() this function can be fed with nodes of type:
2216 * element-, attribute-, text-, cdata-section-, comment- and
2217 * processing-instruction-node.
2219 * Returns: -1 in case of error, 1 if the current state in the stream is a
2220 * match and 0 otherwise.
2223 xmlStreamPushNode(xmlStreamCtxtPtr stream
,
2224 const xmlChar
*name
, const xmlChar
*ns
,
2227 return (xmlStreamPushInternal(stream
, name
, ns
,
2232 * xmlStreamPushAttr:
2233 * @stream: the stream context
2234 * @name: the current name
2235 * @ns: the namespace name
2237 * Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2238 * indicated a dictionary, then strings for name and ns will be expected
2239 * to come from the dictionary.
2240 * Both @name and @ns being NULL means the / i.e. the root of the document.
2241 * This can also act as a reset.
2242 * Otherwise the function will act as if it has been given an attribute-node.
2244 * Returns: -1 in case of error, 1 if the current state in the stream is a
2245 * match and 0 otherwise.
2248 xmlStreamPushAttr(xmlStreamCtxtPtr stream
,
2249 const xmlChar
*name
, const xmlChar
*ns
) {
2250 return (xmlStreamPushInternal(stream
, name
, ns
, XML_ATTRIBUTE_NODE
));
2255 * @stream: the stream context
2257 * push one level from the stream.
2259 * Returns: -1 in case of error, 0 otherwise.
2262 xmlStreamPop(xmlStreamCtxtPtr stream
) {
2267 while (stream
!= NULL
) {
2269 * Reset block-level.
2271 if (stream
->blockLevel
== stream
->level
)
2272 stream
->blockLevel
= -1;
2275 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2276 * (see the thread at
2277 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2282 * Check evolution of existing states
2284 for (i
= stream
->nbState
-1; i
>= 0; i
--) {
2285 /* discard obsoleted states */
2286 lev
= stream
->states
[(2 * i
) + 1];
2287 if (lev
> stream
->level
)
2289 if (lev
<= stream
->level
)
2292 stream
= stream
->next
;
2298 * xmlStreamWantsAnyNode:
2299 * @streamCtxt: the stream context
2301 * Query if the streaming pattern additionally needs to be fed with
2302 * text-, cdata-section-, comment- and processing-instruction-nodes.
2303 * If the result is 0 then only element-nodes and attribute-nodes
2304 * need to be pushed.
2306 * Returns: 1 in case of need of nodes of the above described types,
2307 * 0 otherwise. -1 on API errors.
2310 xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt
)
2312 if (streamCtxt
== NULL
)
2314 while (streamCtxt
!= NULL
) {
2315 if (streamCtxt
->comp
->flags
& XML_STREAM_FINAL_IS_ANY_NODE
)
2317 streamCtxt
= streamCtxt
->next
;
2322 /************************************************************************
2324 * The public interfaces *
2326 ************************************************************************/
2329 * xmlPatterncompile:
2330 * @pattern: the pattern to compile
2331 * @dict: an optional dictionary for interned strings
2332 * @flags: compilation flags, see xmlPatternFlags
2333 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2335 * Compile a pattern.
2337 * Returns the compiled form of the pattern or NULL in case of error
2340 xmlPatterncompile(const xmlChar
*pattern
, xmlDict
*dict
, int flags
,
2341 const xmlChar
**namespaces
) {
2342 xmlPatternPtr ret
= NULL
, cur
;
2343 xmlPatParserContextPtr ctxt
= NULL
;
2344 const xmlChar
*or, *start
;
2345 xmlChar
*tmp
= NULL
;
2349 if (pattern
== NULL
)
2356 while ((*or != 0) && (*or != '|')) or++;
2358 ctxt
= xmlNewPatParserContext(start
, dict
, namespaces
);
2360 tmp
= xmlStrndup(start
, or - start
);
2362 ctxt
= xmlNewPatParserContext(tmp
, dict
, namespaces
);
2366 if (ctxt
== NULL
) goto error
;
2367 cur
= xmlNewPattern();
2368 if (cur
== NULL
) goto error
;
2370 * Assign string dict.
2374 xmlDictReference(dict
);
2379 cur
->next
= ret
->next
;
2385 if (XML_STREAM_XS_IDC(cur
))
2386 xmlCompileIDCXPathPath(ctxt
);
2388 xmlCompilePathPattern(ctxt
);
2389 if (ctxt
->error
!= 0)
2391 xmlFreePatParserContext(ctxt
);
2397 type
= cur
->flags
& (PAT_FROM_ROOT
| PAT_FROM_CUR
);
2398 } else if (type
== PAT_FROM_ROOT
) {
2399 if (cur
->flags
& PAT_FROM_CUR
)
2401 } else if (type
== PAT_FROM_CUR
) {
2402 if (cur
->flags
& PAT_FROM_ROOT
)
2407 xmlStreamCompile(cur
);
2408 if (xmlReversePattern(cur
) < 0)
2416 if (streamable
== 0) {
2418 while (cur
!= NULL
) {
2419 if (cur
->stream
!= NULL
) {
2420 xmlFreeStreamComp(cur
->stream
);
2429 if (ctxt
!= NULL
) xmlFreePatParserContext(ctxt
);
2430 if (ret
!= NULL
) xmlFreePattern(ret
);
2431 if (tmp
!= NULL
) xmlFree(tmp
);
2437 * @comp: the precompiled pattern
2440 * Test whether the node matches the pattern
2442 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2445 xmlPatternMatch(xmlPatternPtr comp
, xmlNodePtr node
)
2449 if ((comp
== NULL
) || (node
== NULL
))
2452 while (comp
!= NULL
) {
2453 ret
= xmlPatMatch(comp
, node
);
2462 * xmlPatternGetStreamCtxt:
2463 * @comp: the precompiled pattern
2465 * Get a streaming context for that pattern
2466 * Use xmlFreeStreamCtxt to free the context.
2468 * Returns a pointer to the context or NULL in case of failure
2471 xmlPatternGetStreamCtxt(xmlPatternPtr comp
)
2473 xmlStreamCtxtPtr ret
= NULL
, cur
;
2475 if ((comp
== NULL
) || (comp
->stream
== NULL
))
2478 while (comp
!= NULL
) {
2479 if (comp
->stream
== NULL
)
2481 cur
= xmlNewStreamCtxt(comp
->stream
);
2487 cur
->next
= ret
->next
;
2490 cur
->flags
= comp
->flags
;
2495 xmlFreeStreamCtxt(ret
);
2500 * xmlPatternStreamable:
2501 * @comp: the precompiled pattern
2503 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2506 * Returns 1 if streamable, 0 if not and -1 in case of error.
2509 xmlPatternStreamable(xmlPatternPtr comp
) {
2512 while (comp
!= NULL
) {
2513 if (comp
->stream
== NULL
)
2521 * xmlPatternMaxDepth:
2522 * @comp: the precompiled pattern
2524 * Check the maximum depth reachable by a pattern
2526 * Returns -2 if no limit (using //), otherwise the depth,
2527 * and -1 in case of error
2530 xmlPatternMaxDepth(xmlPatternPtr comp
) {
2534 while (comp
!= NULL
) {
2535 if (comp
->stream
== NULL
)
2537 for (i
= 0;i
< comp
->stream
->nbStep
;i
++)
2538 if (comp
->stream
->steps
[i
].flags
& XML_STREAM_STEP_DESC
)
2540 if (comp
->stream
->nbStep
> ret
)
2541 ret
= comp
->stream
->nbStep
;
2548 * xmlPatternMinDepth:
2549 * @comp: the precompiled pattern
2551 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2554 * Returns -1 in case of error otherwise the depth,
2558 xmlPatternMinDepth(xmlPatternPtr comp
) {
2562 while (comp
!= NULL
) {
2563 if (comp
->stream
== NULL
)
2565 if (comp
->stream
->nbStep
< ret
)
2566 ret
= comp
->stream
->nbStep
;
2575 * xmlPatternFromRoot:
2576 * @comp: the precompiled pattern
2578 * Check if the pattern must be looked at from the root.
2580 * Returns 1 if true, 0 if false and -1 in case of error
2583 xmlPatternFromRoot(xmlPatternPtr comp
) {
2586 while (comp
!= NULL
) {
2587 if (comp
->stream
== NULL
)
2589 if (comp
->flags
& PAT_FROM_ROOT
)
2597 #endif /* LIBXML_PATTERN_ENABLED */