2 * pattern.c: Implemetation 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 */
42 #define ERROR(a, b, c, d)
43 #define ERROR5(a, b, c, d, e)
45 #define XML_STREAM_STEP_DESC 1
46 #define XML_STREAM_STEP_FINAL 2
47 #define XML_STREAM_STEP_ROOT 4
48 #define XML_STREAM_STEP_ATTR 8
49 #define XML_STREAM_STEP_NODE 16
50 #define XML_STREAM_STEP_IN_SET 32
53 * NOTE: Those private flags (XML_STREAM_xxx) are used
54 * in _xmlStreamCtxt->flag. They extend the public
55 * xmlPatternFlags, so be carefull not to interfere with the
56 * reserved values for xmlPatternFlags.
58 #define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
59 #define XML_STREAM_FROM_ROOT 1<<15
60 #define XML_STREAM_DESC 1<<16
63 * XML_STREAM_ANY_NODE is used for comparison against
64 * xmlElementType enums, to indicate a node of any type.
66 #define XML_STREAM_ANY_NODE 100
68 #define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
72 #define XML_STREAM_XS_IDC(c) ((c)->flags & \
73 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
75 #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
77 #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
79 #define XML_PAT_COPY_NSNAME(c, r, nsname) \
80 if ((c)->comp->dict) \
81 r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
82 else r = xmlStrdup(BAD_CAST nsname);
84 #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
86 typedef struct _xmlStreamStep xmlStreamStep
;
87 typedef xmlStreamStep
*xmlStreamStepPtr
;
88 struct _xmlStreamStep
{
89 int flags
; /* properties of that step */
90 const xmlChar
*name
; /* first string value if NULL accept all */
91 const xmlChar
*ns
; /* second string value */
92 int nodeType
; /* type of node */
95 typedef struct _xmlStreamComp xmlStreamComp
;
96 typedef xmlStreamComp
*xmlStreamCompPtr
;
97 struct _xmlStreamComp
{
98 xmlDict
*dict
; /* the dictionary if any */
99 int nbStep
; /* number of steps in the automata */
100 int maxStep
; /* allocated number of steps */
101 xmlStreamStepPtr steps
; /* the array of steps */
105 struct _xmlStreamCtxt
{
106 struct _xmlStreamCtxt
*next
;/* link to next sub pattern if | */
107 xmlStreamCompPtr comp
; /* the compiled stream */
108 int nbState
; /* number of states in the automata */
109 int maxState
; /* allocated number of states */
110 int level
; /* how deep are we ? */
111 int *states
; /* the array of step indexes */
112 int flags
; /* validation options */
116 static void xmlFreeStreamComp(xmlStreamCompPtr comp
);
135 typedef struct _xmlStepState xmlStepState
;
136 typedef xmlStepState
*xmlStepStatePtr
;
137 struct _xmlStepState
{
142 typedef struct _xmlStepStates xmlStepStates
;
143 typedef xmlStepStates
*xmlStepStatesPtr
;
144 struct _xmlStepStates
{
147 xmlStepStatePtr states
;
150 typedef struct _xmlStepOp xmlStepOp
;
151 typedef xmlStepOp
*xmlStepOpPtr
;
154 const xmlChar
*value
;
155 const xmlChar
*value2
; /* The namespace name */
158 #define PAT_FROM_ROOT (1<<8)
159 #define PAT_FROM_CUR (1<<9)
162 void *data
; /* the associated template */
163 xmlDictPtr dict
; /* the optional dictionary */
164 struct _xmlPattern
*next
; /* next pattern if | is used */
165 const xmlChar
*pattern
; /* the pattern */
166 int flags
; /* flags */
169 xmlStepOpPtr steps
; /* ops for computation */
170 xmlStreamCompPtr stream
; /* the streaming data if any */
173 typedef struct _xmlPatParserContext xmlPatParserContext
;
174 typedef xmlPatParserContext
*xmlPatParserContextPtr
;
175 struct _xmlPatParserContext
{
176 const xmlChar
*cur
; /* the current char being parsed */
177 const xmlChar
*base
; /* the full expression */
178 int error
; /* error code */
179 xmlDictPtr dict
; /* the dictionary if any */
180 xmlPatternPtr comp
; /* the result */
181 xmlNodePtr elem
; /* the current node if any */
182 const xmlChar
**namespaces
; /* the namespaces definitions */
183 int nb_namespaces
; /* the number of namespaces */
186 /************************************************************************
190 ************************************************************************/
195 * Create a new XSLT Pattern
197 * Returns the newly allocated xmlPatternPtr or NULL in case of error
200 xmlNewPattern(void) {
203 cur
= (xmlPatternPtr
) xmlMalloc(sizeof(xmlPattern
));
205 ERROR(NULL
, NULL
, NULL
,
206 "xmlNewPattern : malloc failed\n");
209 memset(cur
, 0, sizeof(xmlPattern
));
211 cur
->steps
= (xmlStepOpPtr
) xmlMalloc(cur
->maxStep
* sizeof(xmlStepOp
));
212 if (cur
->steps
== NULL
) {
214 ERROR(NULL
, NULL
, NULL
,
215 "xmlNewPattern : malloc failed\n");
223 * @comp: an XSLT comp
225 * Free up the memory allocated by @comp
228 xmlFreePattern(xmlPatternPtr comp
) {
234 if (comp
->next
!= NULL
)
235 xmlFreePattern(comp
->next
);
236 if (comp
->stream
!= NULL
)
237 xmlFreeStreamComp(comp
->stream
);
238 if (comp
->pattern
!= NULL
)
239 xmlFree((xmlChar
*)comp
->pattern
);
240 if (comp
->steps
!= NULL
) {
241 if (comp
->dict
== NULL
) {
242 for (i
= 0;i
< comp
->nbStep
;i
++) {
243 op
= &comp
->steps
[i
];
244 if (op
->value
!= NULL
)
245 xmlFree((xmlChar
*) op
->value
);
246 if (op
->value2
!= NULL
)
247 xmlFree((xmlChar
*) op
->value2
);
250 xmlFree(comp
->steps
);
252 if (comp
->dict
!= NULL
)
253 xmlDictFree(comp
->dict
);
255 memset(comp
, -1, sizeof(xmlPattern
));
260 * xmlFreePatternList:
261 * @comp: an XSLT comp list
263 * Free up the memory allocated by all the elements of @comp
266 xmlFreePatternList(xmlPatternPtr comp
) {
269 while (comp
!= NULL
) {
278 * xmlNewPatParserContext:
279 * @pattern: the pattern context
280 * @dict: the inherited dictionary or NULL
281 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
282 * with [NULL, NULL] or NULL if no namespace is used
284 * Create a new XML pattern parser context
286 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
288 static xmlPatParserContextPtr
289 xmlNewPatParserContext(const xmlChar
*pattern
, xmlDictPtr dict
,
290 const xmlChar
**namespaces
) {
291 xmlPatParserContextPtr cur
;
296 cur
= (xmlPatParserContextPtr
) xmlMalloc(sizeof(xmlPatParserContext
));
298 ERROR(NULL
, NULL
, NULL
,
299 "xmlNewPatParserContext : malloc failed\n");
302 memset(cur
, 0, sizeof(xmlPatParserContext
));
306 if (namespaces
!= NULL
) {
308 for (i
= 0;namespaces
[2 * i
] != NULL
;i
++);
309 cur
->nb_namespaces
= i
;
311 cur
->nb_namespaces
= 0;
313 cur
->namespaces
= namespaces
;
318 * xmlFreePatParserContext:
319 * @ctxt: an XSLT parser context
321 * Free up the memory allocated by @ctxt
324 xmlFreePatParserContext(xmlPatParserContextPtr ctxt
) {
327 memset(ctxt
, -1, sizeof(xmlPatParserContext
));
333 * @comp: the compiled match expression
335 * @value: the first value
336 * @value2: the second value
338 * Add a step to an XSLT Compiled Match
340 * Returns -1 in case of failure, 0 otherwise.
343 xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED
,
345 xmlPatOp op
, xmlChar
* value
, xmlChar
* value2
)
347 if (comp
->nbStep
>= comp
->maxStep
) {
349 temp
= (xmlStepOpPtr
) xmlRealloc(comp
->steps
, comp
->maxStep
* 2 *
352 ERROR(ctxt
, NULL
, NULL
,
353 "xmlPatternAdd: realloc failed\n");
359 comp
->steps
[comp
->nbStep
].op
= op
;
360 comp
->steps
[comp
->nbStep
].value
= value
;
361 comp
->steps
[comp
->nbStep
].value2
= value2
;
368 * xsltSwapTopPattern:
369 * @comp: the compiled match expression
371 * reverse the two top steps.
374 xsltSwapTopPattern(xmlPatternPtr comp
) {
376 int j
= comp
->nbStep
- 1;
379 register const xmlChar
*tmp
;
380 register xmlPatOp op
;
382 tmp
= comp
->steps
[i
].value
;
383 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
384 comp
->steps
[j
].value
= tmp
;
385 tmp
= comp
->steps
[i
].value2
;
386 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
387 comp
->steps
[j
].value2
= tmp
;
388 op
= comp
->steps
[i
].op
;
389 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
390 comp
->steps
[j
].op
= op
;
397 * @comp: the compiled match expression
399 * reverse all the stack of expressions
401 * returns 0 in case of success and -1 in case of error.
404 xmlReversePattern(xmlPatternPtr comp
) {
408 * remove the leading // for //a or .//a
410 if ((comp
->nbStep
> 0) && (comp
->steps
[0].op
== XML_OP_ANCESTOR
)) {
411 for (i
= 0, j
= 1;j
< comp
->nbStep
;i
++,j
++) {
412 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
413 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
414 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
418 if (comp
->nbStep
>= comp
->maxStep
) {
420 temp
= (xmlStepOpPtr
) xmlRealloc(comp
->steps
, comp
->maxStep
* 2 *
423 ERROR(ctxt
, NULL
, NULL
,
424 "xmlReversePattern: realloc failed\n");
431 j
= comp
->nbStep
- 1;
433 register const xmlChar
*tmp
;
434 register xmlPatOp op
;
435 tmp
= comp
->steps
[i
].value
;
436 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
437 comp
->steps
[j
].value
= tmp
;
438 tmp
= comp
->steps
[i
].value2
;
439 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
440 comp
->steps
[j
].value2
= tmp
;
441 op
= comp
->steps
[i
].op
;
442 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
443 comp
->steps
[j
].op
= op
;
447 comp
->steps
[comp
->nbStep
].value
= NULL
;
448 comp
->steps
[comp
->nbStep
].value2
= NULL
;
449 comp
->steps
[comp
->nbStep
++].op
= XML_OP_END
;
453 /************************************************************************
455 * The interpreter for the precompiled patterns *
457 ************************************************************************/
460 xmlPatPushState(xmlStepStates
*states
, int step
, xmlNodePtr node
) {
461 if ((states
->states
== NULL
) || (states
->maxstates
<= 0)) {
462 states
->maxstates
= 4;
463 states
->nbstates
= 0;
464 states
->states
= xmlMalloc(4 * sizeof(xmlStepState
));
466 else if (states
->maxstates
<= states
->nbstates
) {
469 tmp
= (xmlStepStatePtr
) xmlRealloc(states
->states
,
470 2 * states
->maxstates
* sizeof(xmlStepState
));
473 states
->states
= tmp
;
474 states
->maxstates
*= 2;
476 states
->states
[states
->nbstates
].step
= step
;
477 states
->states
[states
->nbstates
++].node
= node
;
479 fprintf(stderr
, "Push: %d, %s\n", step
, node
->name
);
486 * @comp: the precompiled pattern
489 * Test whether the node matches the pattern
491 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
494 xmlPatMatch(xmlPatternPtr comp
, xmlNodePtr node
) {
497 xmlStepStates states
= {0, 0, NULL
}; /* // may require backtrack */
499 if ((comp
== NULL
) || (node
== NULL
)) return(-1);
502 for (;i
< comp
->nbStep
;i
++) {
503 step
= &comp
->steps
[i
];
508 if (node
->type
== XML_NAMESPACE_DECL
)
511 if ((node
->type
== XML_DOCUMENT_NODE
) ||
512 #ifdef LIBXML_DOCB_ENABLED
513 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
515 (node
->type
== XML_HTML_DOCUMENT_NODE
))
519 if (node
->type
!= XML_ELEMENT_NODE
)
521 if (step
->value
== NULL
)
523 if (step
->value
[0] != node
->name
[0])
525 if (!xmlStrEqual(step
->value
, node
->name
))
529 if (node
->ns
== NULL
) {
530 if (step
->value2
!= NULL
)
532 } else if (node
->ns
->href
!= NULL
) {
533 if (step
->value2
== NULL
)
535 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
542 if ((node
->type
!= XML_ELEMENT_NODE
) &&
543 (node
->type
!= XML_DOCUMENT_NODE
) &&
544 #ifdef LIBXML_DOCB_ENABLED
545 (node
->type
!= XML_DOCB_DOCUMENT_NODE
) &&
547 (node
->type
!= XML_HTML_DOCUMENT_NODE
))
550 lst
= node
->children
;
552 if (step
->value
!= NULL
) {
553 while (lst
!= NULL
) {
554 if ((lst
->type
== XML_ELEMENT_NODE
) &&
555 (step
->value
[0] == lst
->name
[0]) &&
556 (xmlStrEqual(step
->value
, lst
->name
)))
566 if (node
->type
!= XML_ATTRIBUTE_NODE
)
568 if (step
->value
!= NULL
) {
569 if (step
->value
[0] != node
->name
[0])
571 if (!xmlStrEqual(step
->value
, node
->name
))
575 if (node
->ns
== NULL
) {
576 if (step
->value2
!= NULL
)
578 } else if (step
->value2
!= NULL
) {
579 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
584 if ((node
->type
== XML_DOCUMENT_NODE
) ||
585 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
586 #ifdef LIBXML_DOCB_ENABLED
587 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
589 (node
->type
== XML_NAMESPACE_DECL
))
594 if (step
->value
== NULL
)
596 if (step
->value
[0] != node
->name
[0])
598 if (!xmlStrEqual(step
->value
, node
->name
))
601 if (node
->ns
== NULL
) {
602 if (step
->value2
!= NULL
)
604 } else if (node
->ns
->href
!= NULL
) {
605 if (step
->value2
== NULL
)
607 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
611 case XML_OP_ANCESTOR
:
612 /* TODO: implement coalescing of ANCESTOR/NODE ops */
613 if (step
->value
== NULL
) {
615 step
= &comp
->steps
[i
];
616 if (step
->op
== XML_OP_ROOT
)
618 if (step
->op
!= XML_OP_ELEM
)
620 if (step
->value
== NULL
)
625 if ((node
->type
== XML_DOCUMENT_NODE
) ||
626 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
627 #ifdef LIBXML_DOCB_ENABLED
628 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
630 (node
->type
== XML_NAMESPACE_DECL
))
633 while (node
!= NULL
) {
634 if ((node
->type
== XML_ELEMENT_NODE
) &&
635 (step
->value
[0] == node
->name
[0]) &&
636 (xmlStrEqual(step
->value
, node
->name
))) {
638 if (node
->ns
== NULL
) {
639 if (step
->value2
== NULL
)
641 } else if (node
->ns
->href
!= NULL
) {
642 if ((step
->value2
!= NULL
) &&
643 (xmlStrEqual(step
->value2
, node
->ns
->href
)))
652 * prepare a potential rollback from here
653 * for ancestors of that node.
655 if (step
->op
== XML_OP_ANCESTOR
)
656 xmlPatPushState(&states
, i
, node
);
658 xmlPatPushState(&states
, i
- 1, node
);
661 if (node
->type
!= XML_ELEMENT_NODE
)
663 if (node
->ns
== NULL
) {
664 if (step
->value
!= NULL
)
666 } else if (node
->ns
->href
!= NULL
) {
667 if (step
->value
== NULL
)
669 if (!xmlStrEqual(step
->value
, node
->ns
->href
))
674 if (node
->type
!= XML_ELEMENT_NODE
)
680 if (states
.states
!= NULL
) {
681 /* Free the rollback states */
682 xmlFree(states
.states
);
686 /* got an error try to rollback */
687 if (states
.states
== NULL
)
689 if (states
.nbstates
<= 0) {
690 xmlFree(states
.states
);
694 i
= states
.states
[states
.nbstates
].step
;
695 node
= states
.states
[states
.nbstates
].node
;
697 fprintf(stderr
, "Pop: %d, %s\n", i
, node
->name
);
702 /************************************************************************
704 * Dedicated parser for templates *
706 ************************************************************************/
709 xmlGenericError(xmlGenericErrorContext, \
710 "Unimplemented block at %s:%d\n", \
712 #define CUR (*ctxt->cur)
713 #define SKIP(val) ctxt->cur += (val)
714 #define NXT(val) ctxt->cur[(val)]
715 #define PEEKPREV(val) ctxt->cur[-(val)]
716 #define CUR_PTR ctxt->cur
718 #define SKIP_BLANKS \
719 while (IS_BLANK_CH(CUR)) NEXT
721 #define CURRENT (*ctxt->cur)
722 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
725 #define PUSH(op, val, val2) \
726 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
728 #define XSLT_ERROR(X) \
729 { xsltError(ctxt, __FILE__, __LINE__, X); \
730 ctxt->error = (X); return; }
732 #define XSLT_ERROR0(X) \
733 { xsltError(ctxt, __FILE__, __LINE__, X); \
734 ctxt->error = (X); return(0); }
739 * @ctxt: the XPath Parser context
741 * Parse an XPath Litteral:
743 * [29] Literal ::= '"' [^"]* '"'
746 * Returns the Literal parsed or NULL
750 xmlPatScanLiteral(xmlPatParserContextPtr ctxt
) {
751 const xmlChar
*q
, *cur
;
759 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
760 while ((IS_CHAR(val
)) && (val
!= '"')) {
762 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
769 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
771 ret
= xmlStrndup(q
, cur
- q
);
775 } else if (CUR
== '\'') {
778 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
779 while ((IS_CHAR(val
)) && (val
!= '\'')) {
781 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
788 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
790 ret
= xmlStrndup(q
, cur
- q
);
795 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
805 * @ctxt: the XPath Parser context
807 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
808 * CombiningChar | Extender
810 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
812 * [6] Names ::= Name (S Name)*
814 * Returns the Name parsed or NULL
818 xmlPatScanName(xmlPatParserContextPtr ctxt
) {
819 const xmlChar
*q
, *cur
;
826 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
827 if (!IS_LETTER(val
) && (val
!= '_') && (val
!= ':'))
830 while ((IS_LETTER(val
)) || (IS_DIGIT(val
)) ||
831 (val
== '.') || (val
== '-') ||
833 (IS_COMBINING(val
)) ||
834 (IS_EXTENDER(val
))) {
836 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
839 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
841 ret
= xmlStrndup(q
, cur
- q
);
848 * @ctxt: the XPath Parser context
850 * Parses a non qualified name
852 * Returns the Name parsed or NULL
856 xmlPatScanNCName(xmlPatParserContextPtr ctxt
) {
857 const xmlChar
*q
, *cur
;
864 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
865 if (!IS_LETTER(val
) && (val
!= '_'))
868 while ((IS_LETTER(val
)) || (IS_DIGIT(val
)) ||
869 (val
== '.') || (val
== '-') ||
871 (IS_COMBINING(val
)) ||
872 (IS_EXTENDER(val
))) {
874 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
877 ret
= (xmlChar
*) xmlDictLookup(ctxt
->dict
, q
, cur
- q
);
879 ret
= xmlStrndup(q
, cur
- q
);
887 * @ctxt: the XPath Parser context
888 * @prefix: the place to store the prefix
890 * Parse a qualified name
892 * Returns the Name parsed or NULL
896 xmlPatScanQName(xmlPatParserContextPtr ctxt
, xmlChar
**prefix
) {
900 ret
= xmlPatScanNCName(ctxt
);
904 ret
= xmlPatScanNCName(ctxt
);
911 * xmlCompileAttributeTest:
912 * @ctxt: the compilation context
914 * Compile an attribute test.
917 xmlCompileAttributeTest(xmlPatParserContextPtr ctxt
) {
918 xmlChar
*token
= NULL
;
919 xmlChar
*name
= NULL
;
923 name
= xmlPatScanNCName(ctxt
);
926 PUSH(XML_OP_ATTR
, NULL
, NULL
);
929 ERROR(NULL
, NULL
, NULL
,
930 "xmlCompileAttributeTest : Name expected\n");
937 xmlChar
*prefix
= name
;
941 if (IS_BLANK_CH(CUR
)) {
942 ERROR5(NULL
, NULL
, NULL
, "Invalid QName.\n", NULL
);
943 XML_PAT_FREE_STRING(ctxt
, prefix
);
948 * This is a namespace match
950 token
= xmlPatScanName(ctxt
);
951 if ((prefix
[0] == 'x') &&
952 (prefix
[1] == 'm') &&
953 (prefix
[2] == 'l') &&
956 XML_PAT_COPY_NSNAME(ctxt
, URL
, XML_XML_NAMESPACE
);
958 for (i
= 0;i
< ctxt
->nb_namespaces
;i
++) {
959 if (xmlStrEqual(ctxt
->namespaces
[2 * i
+ 1], prefix
)) {
960 XML_PAT_COPY_NSNAME(ctxt
, URL
, ctxt
->namespaces
[2 * i
])
964 if (i
>= ctxt
->nb_namespaces
) {
965 ERROR5(NULL
, NULL
, NULL
,
966 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
972 XML_PAT_FREE_STRING(ctxt
, prefix
);
976 PUSH(XML_OP_ATTR
, NULL
, URL
);
978 ERROR(NULL
, NULL
, NULL
,
979 "xmlCompileAttributeTest : Name expected\n");
984 PUSH(XML_OP_ATTR
, token
, URL
);
987 PUSH(XML_OP_ATTR
, name
, NULL
);
992 XML_PAT_FREE_STRING(ctxt
, URL
)
994 XML_PAT_FREE_STRING(ctxt
, token
);
998 * xmlCompileStepPattern:
999 * @ctxt: the compilation context
1001 * Compile the Step Pattern and generates a precompiled
1002 * form suitable for fast matching.
1004 * [3] Step ::= '.' | NameTest
1005 * [4] NameTest ::= QName | '*' | NCName ':' '*'
1009 xmlCompileStepPattern(xmlPatParserContextPtr ctxt
) {
1010 xmlChar
*token
= NULL
;
1011 xmlChar
*name
= NULL
;
1012 xmlChar
*URL
= NULL
;
1021 PUSH(XML_OP_ELEM
, NULL
, NULL
);
1028 if (XML_STREAM_XS_IDC_SEL(ctxt
->comp
)) {
1029 ERROR5(NULL
, NULL
, NULL
,
1030 "Unexpected attribute axis in '%s'.\n", ctxt
->base
);
1035 xmlCompileAttributeTest(ctxt
);
1036 if (ctxt
->error
!= 0)
1040 name
= xmlPatScanNCName(ctxt
);
1044 PUSH(XML_OP_ALL
, NULL
, NULL
);
1047 ERROR(NULL
, NULL
, NULL
,
1048 "xmlCompileStepPattern : Name expected\n");
1053 if (IS_BLANK_CH(CUR
)) {
1060 xmlChar
*prefix
= name
;
1063 if (hasBlanks
|| IS_BLANK_CH(CUR
)) {
1064 ERROR5(NULL
, NULL
, NULL
, "Invalid QName.\n", NULL
);
1069 * This is a namespace match
1071 token
= xmlPatScanName(ctxt
);
1072 if ((prefix
[0] == 'x') &&
1073 (prefix
[1] == 'm') &&
1074 (prefix
[2] == 'l') &&
1077 XML_PAT_COPY_NSNAME(ctxt
, URL
, XML_XML_NAMESPACE
)
1079 for (i
= 0;i
< ctxt
->nb_namespaces
;i
++) {
1080 if (xmlStrEqual(ctxt
->namespaces
[2 * i
+ 1], prefix
)) {
1081 XML_PAT_COPY_NSNAME(ctxt
, URL
, ctxt
->namespaces
[2 * i
])
1085 if (i
>= ctxt
->nb_namespaces
) {
1086 ERROR5(NULL
, NULL
, NULL
,
1087 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1093 XML_PAT_FREE_STRING(ctxt
, prefix
);
1095 if (token
== NULL
) {
1098 PUSH(XML_OP_NS
, URL
, NULL
);
1100 ERROR(NULL
, NULL
, NULL
,
1101 "xmlCompileStepPattern : Name expected\n");
1106 PUSH(XML_OP_ELEM
, token
, URL
);
1110 if (xmlStrEqual(name
, (const xmlChar
*) "child")) {
1111 XML_PAT_FREE_STRING(ctxt
, name
);
1112 name
= xmlPatScanName(ctxt
);
1116 PUSH(XML_OP_ALL
, NULL
, NULL
);
1119 ERROR(NULL
, NULL
, NULL
,
1120 "xmlCompileStepPattern : QName expected\n");
1126 xmlChar
*prefix
= name
;
1130 if (IS_BLANK_CH(CUR
)) {
1131 ERROR5(NULL
, NULL
, NULL
, "Invalid QName.\n", NULL
);
1136 * This is a namespace match
1138 token
= xmlPatScanName(ctxt
);
1139 if ((prefix
[0] == 'x') &&
1140 (prefix
[1] == 'm') &&
1141 (prefix
[2] == 'l') &&
1144 XML_PAT_COPY_NSNAME(ctxt
, URL
, XML_XML_NAMESPACE
)
1146 for (i
= 0;i
< ctxt
->nb_namespaces
;i
++) {
1147 if (xmlStrEqual(ctxt
->namespaces
[2 * i
+ 1], prefix
)) {
1148 XML_PAT_COPY_NSNAME(ctxt
, URL
, ctxt
->namespaces
[2 * i
])
1152 if (i
>= ctxt
->nb_namespaces
) {
1153 ERROR5(NULL
, NULL
, NULL
,
1154 "xmlCompileStepPattern : no namespace bound "
1155 "to prefix %s\n", prefix
);
1160 XML_PAT_FREE_STRING(ctxt
, prefix
);
1162 if (token
== NULL
) {
1165 PUSH(XML_OP_NS
, URL
, NULL
);
1167 ERROR(NULL
, NULL
, NULL
,
1168 "xmlCompileStepPattern : Name expected\n");
1173 PUSH(XML_OP_CHILD
, token
, URL
);
1176 PUSH(XML_OP_CHILD
, name
, NULL
);
1178 } else if (xmlStrEqual(name
, (const xmlChar
*) "attribute")) {
1179 XML_PAT_FREE_STRING(ctxt
, name
)
1181 if (XML_STREAM_XS_IDC_SEL(ctxt
->comp
)) {
1182 ERROR5(NULL
, NULL
, NULL
,
1183 "Unexpected attribute axis in '%s'.\n", ctxt
->base
);
1187 xmlCompileAttributeTest(ctxt
);
1188 if (ctxt
->error
!= 0)
1192 ERROR5(NULL
, NULL
, NULL
,
1193 "The 'element' or 'attribute' axis is expected.\n", NULL
);
1198 } else if (CUR
== '*') {
1204 PUSH(XML_OP_ALL
, token
, NULL
);
1206 PUSH(XML_OP_ELEM
, name
, NULL
);
1211 XML_PAT_FREE_STRING(ctxt
, URL
)
1213 XML_PAT_FREE_STRING(ctxt
, token
)
1215 XML_PAT_FREE_STRING(ctxt
, name
)
1219 * xmlCompilePathPattern:
1220 * @ctxt: the compilation context
1222 * Compile the Path Pattern and generates a precompiled
1223 * form suitable for fast matching.
1225 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1228 xmlCompilePathPattern(xmlPatParserContextPtr ctxt
) {
1231 ctxt
->comp
->flags
|= PAT_FROM_ROOT
;
1232 } else if ((CUR
== '.') || (ctxt
->comp
->flags
& XML_PATTERN_NOTPATTERN
)) {
1233 ctxt
->comp
->flags
|= PAT_FROM_CUR
;
1236 if ((CUR
== '/') && (NXT(1) == '/')) {
1237 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1240 } else if ((CUR
== '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1241 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1245 /* Check for incompleteness. */
1248 ERROR5(NULL
, NULL
, NULL
,
1249 "Incomplete expression '%s'.\n", ctxt
->base
);
1256 xmlCompileAttributeTest(ctxt
);
1258 /* TODO: check for incompleteness */
1260 xmlCompileStepPattern(ctxt
);
1261 if (ctxt
->error
!= 0)
1266 PUSH(XML_OP_ROOT
, NULL
, NULL
);
1268 /* Check for incompleteness. */
1271 ERROR5(NULL
, NULL
, NULL
,
1272 "Incomplete expression '%s'.\n", ctxt
->base
);
1277 xmlCompileStepPattern(ctxt
);
1278 if (ctxt
->error
!= 0)
1281 while (CUR
== '/') {
1282 if (NXT(1) == '/') {
1283 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1287 xmlCompileStepPattern(ctxt
);
1288 if (ctxt
->error
!= 0)
1291 PUSH(XML_OP_PARENT
, NULL
, NULL
);
1295 ERROR5(NULL
, NULL
, NULL
,
1296 "Incomplete expression '%s'.\n", ctxt
->base
);
1300 xmlCompileStepPattern(ctxt
);
1301 if (ctxt
->error
!= 0)
1307 ERROR5(NULL
, NULL
, NULL
,
1308 "Failed to compile pattern %s\n", ctxt
->base
);
1316 * xmlCompileIDCXPathPath:
1317 * @ctxt: the compilation context
1319 * Compile the Path Pattern and generates a precompiled
1320 * form suitable for fast matching.
1322 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1325 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt
) {
1328 ERROR5(NULL
, NULL
, NULL
,
1329 "Unexpected selection of the document root in '%s'.\n",
1333 ctxt
->comp
->flags
|= PAT_FROM_CUR
;
1336 /* "." - "self::node()" */
1341 * Selection of the context node.
1343 PUSH(XML_OP_ELEM
, NULL
, NULL
);
1347 /* TODO: A more meaningful error message. */
1348 ERROR5(NULL
, NULL
, NULL
,
1349 "Unexpected token after '.' in '%s'.\n", ctxt
->base
);
1352 /* "./" - "self::node()/" */
1356 if (IS_BLANK_CH(PEEKPREV(1))) {
1360 ERROR5(NULL
, NULL
, NULL
,
1361 "Unexpected '/' token in '%s'.\n", ctxt
->base
);
1364 /* ".//" - "self:node()/descendant-or-self::node()/" */
1365 PUSH(XML_OP_ANCESTOR
, NULL
, NULL
);
1370 goto error_unfinished
;
1376 xmlCompileStepPattern(ctxt
);
1377 if (ctxt
->error
!= 0)
1382 PUSH(XML_OP_PARENT
, NULL
, NULL
);
1387 * Disallow subsequent '//'.
1389 ERROR5(NULL
, NULL
, NULL
,
1390 "Unexpected subsequent '//' in '%s'.\n",
1395 goto error_unfinished
;
1400 ERROR5(NULL
, NULL
, NULL
,
1401 "Failed to compile expression '%s'.\n", ctxt
->base
);
1411 ERROR5(NULL
, NULL
, NULL
,
1412 "Unfinished expression '%s'.\n", ctxt
->base
);
1416 /************************************************************************
1418 * The streaming code *
1420 ************************************************************************/
1422 #ifdef DEBUG_STREAMING
1424 xmlDebugStreamComp(xmlStreamCompPtr stream
) {
1427 if (stream
== NULL
) {
1428 printf("Stream: NULL\n");
1431 printf("Stream: %d steps\n", stream
->nbStep
);
1432 for (i
= 0;i
< stream
->nbStep
;i
++) {
1433 if (stream
->steps
[i
].ns
!= NULL
) {
1434 printf("{%s}", stream
->steps
[i
].ns
);
1436 if (stream
->steps
[i
].name
== NULL
) {
1439 printf("%s ", stream
->steps
[i
].name
);
1441 if (stream
->steps
[i
].flags
& XML_STREAM_STEP_ROOT
)
1443 if (stream
->steps
[i
].flags
& XML_STREAM_STEP_DESC
)
1445 if (stream
->steps
[i
].flags
& XML_STREAM_STEP_FINAL
)
1451 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt
, int match
) {
1455 printf("Stream: NULL\n");
1458 printf("Stream: level %d, %d states: ", ctxt
->level
, ctxt
->nbState
);
1460 printf("matches\n");
1463 for (i
= 0;i
< ctxt
->nbState
;i
++) {
1464 if (ctxt
->states
[2 * i
] < 0)
1465 printf(" %d: free\n", i
);
1467 printf(" %d: step %d, level %d", i
, ctxt
->states
[2 * i
],
1468 ctxt
->states
[(2 * i
) + 1]);
1469 if (ctxt
->comp
->steps
[ctxt
->states
[2 * i
]].flags
&
1470 XML_STREAM_STEP_DESC
)
1480 * @size: the number of expected steps
1482 * build a new compiled pattern for streaming
1484 * Returns the new structure or NULL in case of error.
1486 static xmlStreamCompPtr
1487 xmlNewStreamComp(int size
) {
1488 xmlStreamCompPtr cur
;
1493 cur
= (xmlStreamCompPtr
) xmlMalloc(sizeof(xmlStreamComp
));
1495 ERROR(NULL
, NULL
, NULL
,
1496 "xmlNewStreamComp: malloc failed\n");
1499 memset(cur
, 0, sizeof(xmlStreamComp
));
1500 cur
->steps
= (xmlStreamStepPtr
) xmlMalloc(size
* sizeof(xmlStreamStep
));
1501 if (cur
->steps
== NULL
) {
1503 ERROR(NULL
, NULL
, NULL
,
1504 "xmlNewStreamComp: malloc failed\n");
1508 cur
->maxStep
= size
;
1513 * xmlFreeStreamComp:
1514 * @comp: the compiled pattern for streaming
1516 * Free the compiled pattern for streaming
1519 xmlFreeStreamComp(xmlStreamCompPtr comp
) {
1521 if (comp
->steps
!= NULL
)
1522 xmlFree(comp
->steps
);
1523 if (comp
->dict
!= NULL
)
1524 xmlDictFree(comp
->dict
);
1530 * xmlStreamCompAddStep:
1531 * @comp: the compiled pattern for streaming
1532 * @name: the first string, the name, or NULL for *
1533 * @ns: the second step, the namespace name
1534 * @flags: the flags for that step
1536 * Add a new step to the compiled pattern
1538 * Returns -1 in case of error or the step index if successful
1541 xmlStreamCompAddStep(xmlStreamCompPtr comp
, const xmlChar
*name
,
1542 const xmlChar
*ns
, int nodeType
, int flags
) {
1543 xmlStreamStepPtr cur
;
1545 if (comp
->nbStep
>= comp
->maxStep
) {
1546 cur
= (xmlStreamStepPtr
) xmlRealloc(comp
->steps
,
1547 comp
->maxStep
* 2 * sizeof(xmlStreamStep
));
1549 ERROR(NULL
, NULL
, NULL
,
1550 "xmlNewStreamComp: malloc failed\n");
1556 cur
= &comp
->steps
[comp
->nbStep
++];
1560 cur
->nodeType
= nodeType
;
1561 return(comp
->nbStep
- 1);
1566 * @comp: the precompiled pattern
1568 * Tries to stream compile a pattern
1570 * Returns -1 in case of failure and 0 in case of success.
1573 xmlStreamCompile(xmlPatternPtr comp
) {
1574 xmlStreamCompPtr stream
;
1575 int i
, s
= 0, root
= 0, flags
= 0, prevs
= -1;
1578 if ((comp
== NULL
) || (comp
->steps
== NULL
))
1581 * special case for .
1583 if ((comp
->nbStep
== 1) &&
1584 (comp
->steps
[0].op
== XML_OP_ELEM
) &&
1585 (comp
->steps
[0].value
== NULL
) &&
1586 (comp
->steps
[0].value2
== NULL
)) {
1587 stream
= xmlNewStreamComp(0);
1590 /* Note that the stream will have no steps in this case. */
1591 stream
->flags
|= XML_STREAM_FINAL_IS_ANY_NODE
;
1592 comp
->stream
= stream
;
1596 stream
= xmlNewStreamComp((comp
->nbStep
/ 2) + 1);
1599 if (comp
->dict
!= NULL
) {
1600 stream
->dict
= comp
->dict
;
1601 xmlDictReference(stream
->dict
);
1605 if (comp
->flags
& PAT_FROM_ROOT
)
1606 stream
->flags
|= XML_STREAM_FROM_ROOT
;
1608 for (;i
< comp
->nbStep
;i
++) {
1609 step
= comp
->steps
[i
];
1619 s
= xmlStreamCompAddStep(stream
, NULL
, step
.value
,
1620 XML_ELEMENT_NODE
, flags
);
1627 flags
|= XML_STREAM_STEP_ATTR
;
1629 s
= xmlStreamCompAddStep(stream
,
1630 step
.value
, step
.value2
, XML_ATTRIBUTE_NODE
, flags
);
1636 if ((step
.value
== NULL
) && (step
.value2
== NULL
)) {
1638 * We have a "." or "self::node()" here.
1639 * Eliminate redundant self::node() tests like in "/./."
1641 * The only case we won't eliminate is "//.", i.e. if
1642 * self::node() is the last node test and we had
1643 * continuation somewhere beforehand.
1645 if ((comp
->nbStep
== i
+ 1) &&
1646 (flags
& XML_STREAM_STEP_DESC
)) {
1648 * Mark the special case where the expression resolves
1649 * to any type of node.
1651 if (comp
->nbStep
== i
+ 1) {
1652 stream
->flags
|= XML_STREAM_FINAL_IS_ANY_NODE
;
1654 flags
|= XML_STREAM_STEP_NODE
;
1655 s
= xmlStreamCompAddStep(stream
, NULL
, NULL
,
1656 XML_STREAM_ANY_NODE
, flags
);
1661 * If there was a previous step, mark it to be added to
1662 * the result node-set; this is needed since only
1663 * the last step will be marked as "final" and only
1664 * "final" nodes are added to the resulting set.
1667 stream
->steps
[prevs
].flags
|= XML_STREAM_STEP_IN_SET
;
1673 /* Just skip this one. */
1677 /* An element node. */
1678 s
= xmlStreamCompAddStep(stream
, step
.value
, step
.value2
,
1679 XML_ELEMENT_NODE
, flags
);
1686 /* An element node child. */
1687 s
= xmlStreamCompAddStep(stream
, step
.value
, step
.value2
,
1688 XML_ELEMENT_NODE
, flags
);
1695 s
= xmlStreamCompAddStep(stream
, NULL
, NULL
,
1696 XML_ELEMENT_NODE
, flags
);
1704 case XML_OP_ANCESTOR
:
1705 /* Skip redundant continuations. */
1706 if (flags
& XML_STREAM_STEP_DESC
)
1708 flags
|= XML_STREAM_STEP_DESC
;
1710 * Mark the expression as having "//".
1712 if ((stream
->flags
& XML_STREAM_DESC
) == 0)
1713 stream
->flags
|= XML_STREAM_DESC
;
1717 if ((! root
) && (comp
->flags
& XML_PATTERN_NOTPATTERN
) == 0) {
1719 * If this should behave like a real pattern, we will mark
1720 * the first step as having "//", to be reentrant on every
1723 if ((stream
->flags
& XML_STREAM_DESC
) == 0)
1724 stream
->flags
|= XML_STREAM_DESC
;
1726 if (stream
->nbStep
> 0) {
1727 if ((stream
->steps
[0].flags
& XML_STREAM_STEP_DESC
) == 0)
1728 stream
->steps
[0].flags
|= XML_STREAM_STEP_DESC
;
1731 if (stream
->nbStep
<= s
)
1733 stream
->steps
[s
].flags
|= XML_STREAM_STEP_FINAL
;
1735 stream
->steps
[0].flags
|= XML_STREAM_STEP_ROOT
;
1736 #ifdef DEBUG_STREAMING
1737 xmlDebugStreamComp(stream
);
1739 comp
->stream
= stream
;
1742 xmlFreeStreamComp(stream
);
1748 * @size: the number of expected states
1750 * build a new stream context
1752 * Returns the new structure or NULL in case of error.
1754 static xmlStreamCtxtPtr
1755 xmlNewStreamCtxt(xmlStreamCompPtr stream
) {
1756 xmlStreamCtxtPtr cur
;
1758 cur
= (xmlStreamCtxtPtr
) xmlMalloc(sizeof(xmlStreamCtxt
));
1760 ERROR(NULL
, NULL
, NULL
,
1761 "xmlNewStreamCtxt: malloc failed\n");
1764 memset(cur
, 0, sizeof(xmlStreamCtxt
));
1765 cur
->states
= (int *) xmlMalloc(4 * 2 * sizeof(int));
1766 if (cur
->states
== NULL
) {
1768 ERROR(NULL
, NULL
, NULL
,
1769 "xmlNewStreamCtxt: malloc failed\n");
1776 cur
->blockLevel
= -1;
1781 * xmlFreeStreamCtxt:
1782 * @stream: the stream context
1784 * Free the stream context
1787 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream
) {
1788 xmlStreamCtxtPtr next
;
1790 while (stream
!= NULL
) {
1791 next
= stream
->next
;
1792 if (stream
->states
!= NULL
)
1793 xmlFree(stream
->states
);
1800 * xmlStreamCtxtAddState:
1801 * @comp: the stream context
1802 * @idx: the step index for that streaming state
1804 * Add a new state to the stream context
1806 * Returns -1 in case of error or the state index if successful
1809 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp
, int idx
, int level
) {
1811 for (i
= 0;i
< comp
->nbState
;i
++) {
1812 if (comp
->states
[2 * i
] < 0) {
1813 comp
->states
[2 * i
] = idx
;
1814 comp
->states
[2 * i
+ 1] = level
;
1818 if (comp
->nbState
>= comp
->maxState
) {
1821 cur
= (int *) xmlRealloc(comp
->states
,
1822 comp
->maxState
* 4 * sizeof(int));
1824 ERROR(NULL
, NULL
, NULL
,
1825 "xmlNewStreamCtxt: malloc failed\n");
1829 comp
->maxState
*= 2;
1831 comp
->states
[2 * comp
->nbState
] = idx
;
1832 comp
->states
[2 * comp
->nbState
++ + 1] = level
;
1833 return(comp
->nbState
- 1);
1837 * xmlStreamPushInternal:
1838 * @stream: the stream context
1839 * @name: the current name
1840 * @ns: the namespace name
1841 * @nodeType: the type of the node
1843 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1844 * indicated a dictionary, then strings for name and ns will be expected
1845 * to come from the dictionary.
1846 * Both @name and @ns being NULL means the / i.e. the root of the document.
1847 * This can also act as a reset.
1849 * Returns: -1 in case of error, 1 if the current state in the stream is a
1850 * match and 0 otherwise.
1853 xmlStreamPushInternal(xmlStreamCtxtPtr stream
,
1854 const xmlChar
*name
, const xmlChar
*ns
,
1856 int ret
= 0, err
= 0, final
= 0, tmp
, i
, m
, match
, stepNr
, desc
;
1857 xmlStreamCompPtr comp
;
1859 #ifdef DEBUG_STREAMING
1860 xmlStreamCtxtPtr orig
= stream
;
1863 if ((stream
== NULL
) || (stream
->nbState
< 0))
1866 while (stream
!= NULL
) {
1867 comp
= stream
->comp
;
1869 if ((nodeType
== XML_ELEMENT_NODE
) &&
1870 (name
== NULL
) && (ns
== NULL
)) {
1871 /* We have a document node here (or a reset). */
1872 stream
->nbState
= 0;
1874 stream
->blockLevel
= -1;
1875 if (comp
->flags
& XML_STREAM_FROM_ROOT
) {
1876 if (comp
->nbStep
== 0) {
1877 /* TODO: We have a "/." here? */
1880 if ((comp
->nbStep
== 1) &&
1881 (comp
->steps
[0].nodeType
== XML_STREAM_ANY_NODE
) &&
1882 (comp
->steps
[0].flags
& XML_STREAM_STEP_DESC
))
1885 * In the case of "//." the document node will match
1889 } else if (comp
->steps
[0].flags
& XML_STREAM_STEP_ROOT
) {
1890 /* TODO: Do we need this ? */
1891 tmp
= xmlStreamCtxtAddState(stream
, 0, 0);
1897 stream
= stream
->next
;
1898 continue; /* while */
1902 * Fast check for ".".
1904 if (comp
->nbStep
== 0) {
1906 * / and . are handled at the XPath node set creation
1907 * level by checking min depth
1909 if (stream
->flags
& XML_PATTERN_XPATH
) {
1910 stream
= stream
->next
;
1911 continue; /* while */
1914 * For non-pattern like evaluation like XML Schema IDCs
1915 * or traditional XPath expressions, this will match if
1916 * we are at the first level only, otherwise on every level.
1918 if ((nodeType
!= XML_ATTRIBUTE_NODE
) &&
1919 (((stream
->flags
& XML_PATTERN_NOTPATTERN
) == 0) ||
1920 (stream
->level
== 0))) {
1926 if (stream
->blockLevel
!= -1) {
1928 * Skip blocked expressions.
1934 if ((nodeType
!= XML_ELEMENT_NODE
) &&
1935 (nodeType
!= XML_ATTRIBUTE_NODE
) &&
1936 ((comp
->flags
& XML_STREAM_FINAL_IS_ANY_NODE
) == 0)) {
1938 * No need to process nodes of other types if we don't
1939 * resolve to those types.
1940 * TODO: Do we need to block the context here?
1947 * Check evolution of existing states
1950 m
= stream
->nbState
;
1952 if ((comp
->flags
& XML_STREAM_DESC
) == 0) {
1954 * If there is no "//", then only the last
1955 * added state is of interest.
1957 stepNr
= stream
->states
[2 * (stream
->nbState
-1)];
1959 * TODO: Security check, should not happen, remove it.
1961 if (stream
->states
[(2 * (stream
->nbState
-1)) + 1] <
1970 * If there are "//", then we need to process every "//"
1971 * occuring in the states, plus any other state for this
1974 stepNr
= stream
->states
[2 * i
];
1976 /* TODO: should not happen anymore: dead states */
1980 tmp
= stream
->states
[(2 * i
) + 1];
1982 /* skip new states just added */
1983 if (tmp
> stream
->level
)
1986 /* skip states at ancestor levels, except if "//" */
1987 desc
= comp
->steps
[stepNr
].flags
& XML_STREAM_STEP_DESC
;
1988 if ((tmp
< stream
->level
) && (!desc
))
1992 * Check for correct node-type.
1994 step
= comp
->steps
[stepNr
];
1995 if (step
.nodeType
!= nodeType
) {
1996 if (step
.nodeType
== XML_ATTRIBUTE_NODE
) {
1998 * Block this expression for deeper evaluation.
2000 if ((comp
->flags
& XML_STREAM_DESC
) == 0)
2001 stream
->blockLevel
= stream
->level
+1;
2003 } else if (step
.nodeType
!= XML_STREAM_ANY_NODE
)
2007 * Compare local/namespace-name.
2010 if (step
.nodeType
== XML_STREAM_ANY_NODE
) {
2012 } else if (step
.name
== NULL
) {
2013 if (step
.ns
== NULL
) {
2015 * This lets through all elements/attributes.
2018 } else if (ns
!= NULL
)
2019 match
= xmlStrEqual(step
.ns
, ns
);
2020 } else if (((step
.ns
!= NULL
) == (ns
!= NULL
)) &&
2022 (step
.name
[0] == name
[0]) &&
2023 xmlStrEqual(step
.name
, name
) &&
2024 ((step
.ns
== ns
) || xmlStrEqual(step
.ns
, ns
)))
2030 * TODO: Pointer comparison won't work, since not guaranteed that the given
2031 * values are in the same dict; especially if it's the namespace name,
2032 * normally coming from ns->href. We need a namespace dict mechanism !
2034 } else if (comp
->dict
) {
2035 if (step
.name
== NULL
) {
2036 if (step
.ns
== NULL
)
2039 match
= (step
.ns
== ns
);
2041 match
= ((step
.name
== name
) && (step
.ns
== ns
));
2043 #endif /* if 0 ------------------------------------------------------- */
2045 final
= step
.flags
& XML_STREAM_STEP_FINAL
;
2050 /* descending match create a new state */
2051 xmlStreamCtxtAddState(stream
, stepNr
+ 1,
2058 xmlStreamCtxtAddState(stream
, stepNr
+ 1,
2062 if ((ret
!= 1) && (step
.flags
& XML_STREAM_STEP_IN_SET
)) {
2064 * Check if we have a special case like "foo/bar//.", where
2065 * "foo" is selected as well.
2070 if (((comp
->flags
& XML_STREAM_DESC
) == 0) &&
2071 ((! match
) || final
)) {
2073 * Mark this expression as blocked for any evaluation at
2074 * deeper levels. Note that this includes "/foo"
2075 * expressions if the *pattern* behaviour is used.
2077 stream
->blockLevel
= stream
->level
+1;
2086 * Re/enter the expression.
2087 * Don't reenter if it's an absolute expression like "/foo",
2090 step
= comp
->steps
[0];
2091 if (step
.flags
& XML_STREAM_STEP_ROOT
)
2094 desc
= step
.flags
& XML_STREAM_STEP_DESC
;
2095 if (stream
->flags
& XML_PATTERN_NOTPATTERN
) {
2097 * Re/enter the expression if it is a "descendant" one,
2098 * or if we are at the 1st level of evaluation.
2101 if (stream
->level
== 1) {
2102 if (XML_STREAM_XS_IDC(stream
)) {
2104 * XS-IDC: The missing "self::node()" will always
2105 * match the first given node.
2112 * A "//" is always reentrant.
2118 * XS-IDC: Process the 2nd level, since the missing
2119 * "self::node()" is responsible for the 2nd level being
2120 * the real start level.
2122 if ((stream
->level
== 2) && XML_STREAM_XS_IDC(stream
))
2130 * Check expected node-type.
2132 if (step
.nodeType
!= nodeType
) {
2133 if (nodeType
== XML_ATTRIBUTE_NODE
)
2135 else if (step
.nodeType
!= XML_STREAM_ANY_NODE
)
2139 * Compare local/namespace-name.
2142 if (step
.nodeType
== XML_STREAM_ANY_NODE
) {
2144 } else if (step
.name
== NULL
) {
2145 if (step
.ns
== NULL
) {
2147 * This lets through all elements/attributes.
2150 } else if (ns
!= NULL
)
2151 match
= xmlStrEqual(step
.ns
, ns
);
2152 } else if (((step
.ns
!= NULL
) == (ns
!= NULL
)) &&
2154 (step
.name
[0] == name
[0]) &&
2155 xmlStrEqual(step
.name
, name
) &&
2156 ((step
.ns
== ns
) || xmlStrEqual(step
.ns
, ns
)))
2160 final
= step
.flags
& XML_STREAM_STEP_FINAL
;
2165 xmlStreamCtxtAddState(stream
, 1, stream
->level
);
2166 if ((ret
!= 1) && (step
.flags
& XML_STREAM_STEP_IN_SET
)) {
2168 * Check if we have a special case like "foo//.", where
2169 * "foo" is selected as well.
2174 if (((comp
->flags
& XML_STREAM_DESC
) == 0) &&
2175 ((! match
) || final
)) {
2177 * Mark this expression as blocked for any evaluation at
2180 stream
->blockLevel
= stream
->level
;
2184 stream
= stream
->next
;
2185 } /* while stream != NULL */
2189 #ifdef DEBUG_STREAMING
2190 xmlDebugStreamCtxt(orig
, ret
);
2197 * @stream: the stream context
2198 * @name: the current name
2199 * @ns: the namespace name
2201 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2202 * indicated a dictionary, then strings for name and ns will be expected
2203 * to come from the dictionary.
2204 * Both @name and @ns being NULL means the / i.e. the root of the document.
2205 * This can also act as a reset.
2206 * Otherwise the function will act as if it has been given an element-node.
2208 * Returns: -1 in case of error, 1 if the current state in the stream is a
2209 * match and 0 otherwise.
2212 xmlStreamPush(xmlStreamCtxtPtr stream
,
2213 const xmlChar
*name
, const xmlChar
*ns
) {
2214 return (xmlStreamPushInternal(stream
, name
, ns
, (int) XML_ELEMENT_NODE
));
2218 * xmlStreamPushNode:
2219 * @stream: the stream context
2220 * @name: the current name
2221 * @ns: the namespace name
2222 * @nodeType: the type of the node being pushed
2224 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2225 * indicated a dictionary, then strings for name and ns will be expected
2226 * to come from the dictionary.
2227 * Both @name and @ns being NULL means the / i.e. the root of the document.
2228 * This can also act as a reset.
2229 * Different from xmlStreamPush() this function can be fed with nodes of type:
2230 * element-, attribute-, text-, cdata-section-, comment- and
2231 * processing-instruction-node.
2233 * Returns: -1 in case of error, 1 if the current state in the stream is a
2234 * match and 0 otherwise.
2237 xmlStreamPushNode(xmlStreamCtxtPtr stream
,
2238 const xmlChar
*name
, const xmlChar
*ns
,
2241 return (xmlStreamPushInternal(stream
, name
, ns
,
2246 * xmlStreamPushAttr:
2247 * @stream: the stream context
2248 * @name: the current name
2249 * @ns: the namespace name
2251 * Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2252 * indicated a dictionary, then strings for name and ns will be expected
2253 * to come from the dictionary.
2254 * Both @name and @ns being NULL means the / i.e. the root of the document.
2255 * This can also act as a reset.
2256 * Otherwise the function will act as if it has been given an attribute-node.
2258 * Returns: -1 in case of error, 1 if the current state in the stream is a
2259 * match and 0 otherwise.
2262 xmlStreamPushAttr(xmlStreamCtxtPtr stream
,
2263 const xmlChar
*name
, const xmlChar
*ns
) {
2264 return (xmlStreamPushInternal(stream
, name
, ns
, (int) XML_ATTRIBUTE_NODE
));
2269 * @stream: the stream context
2271 * push one level from the stream.
2273 * Returns: -1 in case of error, 0 otherwise.
2276 xmlStreamPop(xmlStreamCtxtPtr stream
) {
2281 while (stream
!= NULL
) {
2283 * Reset block-level.
2285 if (stream
->blockLevel
== stream
->level
)
2286 stream
->blockLevel
= -1;
2289 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2290 * (see the thread at
2291 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2296 * Check evolution of existing states
2298 for (i
= stream
->nbState
-1; i
>= 0; i
--) {
2299 /* discard obsoleted states */
2300 lev
= stream
->states
[(2 * i
) + 1];
2301 if (lev
> stream
->level
)
2303 if (lev
<= stream
->level
)
2306 stream
= stream
->next
;
2312 * xmlStreamWantsAnyNode:
2313 * @streamCtxt: the stream context
2315 * Query if the streaming pattern additionally needs to be fed with
2316 * text-, cdata-section-, comment- and processing-instruction-nodes.
2317 * If the result is 0 then only element-nodes and attribute-nodes
2318 * need to be pushed.
2320 * Returns: 1 in case of need of nodes of the above described types,
2321 * 0 otherwise. -1 on API errors.
2324 xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt
)
2326 if (streamCtxt
== NULL
)
2328 while (streamCtxt
!= NULL
) {
2329 if (streamCtxt
->comp
->flags
& XML_STREAM_FINAL_IS_ANY_NODE
)
2331 streamCtxt
= streamCtxt
->next
;
2336 /************************************************************************
2338 * The public interfaces *
2340 ************************************************************************/
2343 * xmlPatterncompile:
2344 * @pattern: the pattern to compile
2345 * @dict: an optional dictionary for interned strings
2346 * @flags: compilation flags, see xmlPatternFlags
2347 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2349 * Compile a pattern.
2351 * Returns the compiled form of the pattern or NULL in case of error
2354 xmlPatterncompile(const xmlChar
*pattern
, xmlDict
*dict
, int flags
,
2355 const xmlChar
**namespaces
) {
2356 xmlPatternPtr ret
= NULL
, cur
;
2357 xmlPatParserContextPtr ctxt
= NULL
;
2358 const xmlChar
*or, *start
;
2359 xmlChar
*tmp
= NULL
;
2363 if (pattern
== NULL
)
2370 while ((*or != 0) && (*or != '|')) or++;
2372 ctxt
= xmlNewPatParserContext(start
, dict
, namespaces
);
2374 tmp
= xmlStrndup(start
, or - start
);
2376 ctxt
= xmlNewPatParserContext(tmp
, dict
, namespaces
);
2380 if (ctxt
== NULL
) goto error
;
2381 cur
= xmlNewPattern();
2382 if (cur
== NULL
) goto error
;
2384 * Assign string dict.
2388 xmlDictReference(dict
);
2393 cur
->next
= ret
->next
;
2399 if (XML_STREAM_XS_IDC(cur
))
2400 xmlCompileIDCXPathPath(ctxt
);
2402 xmlCompilePathPattern(ctxt
);
2403 if (ctxt
->error
!= 0)
2405 xmlFreePatParserContext(ctxt
);
2411 type
= cur
->flags
& (PAT_FROM_ROOT
| PAT_FROM_CUR
);
2412 } else if (type
== PAT_FROM_ROOT
) {
2413 if (cur
->flags
& PAT_FROM_CUR
)
2415 } else if (type
== PAT_FROM_CUR
) {
2416 if (cur
->flags
& PAT_FROM_ROOT
)
2421 xmlStreamCompile(cur
);
2422 if (xmlReversePattern(cur
) < 0)
2430 if (streamable
== 0) {
2432 while (cur
!= NULL
) {
2433 if (cur
->stream
!= NULL
) {
2434 xmlFreeStreamComp(cur
->stream
);
2443 if (ctxt
!= NULL
) xmlFreePatParserContext(ctxt
);
2444 if (ret
!= NULL
) xmlFreePattern(ret
);
2445 if (tmp
!= NULL
) xmlFree(tmp
);
2451 * @comp: the precompiled pattern
2454 * Test whether the node matches the pattern
2456 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2459 xmlPatternMatch(xmlPatternPtr comp
, xmlNodePtr node
)
2463 if ((comp
== NULL
) || (node
== NULL
))
2466 while (comp
!= NULL
) {
2467 ret
= xmlPatMatch(comp
, node
);
2476 * xmlPatternGetStreamCtxt:
2477 * @comp: the precompiled pattern
2479 * Get a streaming context for that pattern
2480 * Use xmlFreeStreamCtxt to free the context.
2482 * Returns a pointer to the context or NULL in case of failure
2485 xmlPatternGetStreamCtxt(xmlPatternPtr comp
)
2487 xmlStreamCtxtPtr ret
= NULL
, cur
;
2489 if ((comp
== NULL
) || (comp
->stream
== NULL
))
2492 while (comp
!= NULL
) {
2493 if (comp
->stream
== NULL
)
2495 cur
= xmlNewStreamCtxt(comp
->stream
);
2501 cur
->next
= ret
->next
;
2504 cur
->flags
= comp
->flags
;
2509 xmlFreeStreamCtxt(ret
);
2514 * xmlPatternStreamable:
2515 * @comp: the precompiled pattern
2517 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2520 * Returns 1 if streamable, 0 if not and -1 in case of error.
2523 xmlPatternStreamable(xmlPatternPtr comp
) {
2526 while (comp
!= NULL
) {
2527 if (comp
->stream
== NULL
)
2535 * xmlPatternMaxDepth:
2536 * @comp: the precompiled pattern
2538 * Check the maximum depth reachable by a pattern
2540 * Returns -2 if no limit (using //), otherwise the depth,
2541 * and -1 in case of error
2544 xmlPatternMaxDepth(xmlPatternPtr comp
) {
2548 while (comp
!= NULL
) {
2549 if (comp
->stream
== NULL
)
2551 for (i
= 0;i
< comp
->stream
->nbStep
;i
++)
2552 if (comp
->stream
->steps
[i
].flags
& XML_STREAM_STEP_DESC
)
2554 if (comp
->stream
->nbStep
> ret
)
2555 ret
= comp
->stream
->nbStep
;
2562 * xmlPatternMinDepth:
2563 * @comp: the precompiled pattern
2565 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2568 * Returns -1 in case of error otherwise the depth,
2572 xmlPatternMinDepth(xmlPatternPtr comp
) {
2576 while (comp
!= NULL
) {
2577 if (comp
->stream
== NULL
)
2579 if (comp
->stream
->nbStep
< ret
)
2580 ret
= comp
->stream
->nbStep
;
2589 * xmlPatternFromRoot:
2590 * @comp: the precompiled pattern
2592 * Check if the pattern must be looked at from the root.
2594 * Returns 1 if true, 0 if false and -1 in case of error
2597 xmlPatternFromRoot(xmlPatternPtr comp
) {
2600 while (comp
!= NULL
) {
2601 if (comp
->stream
== NULL
)
2603 if (comp
->flags
& PAT_FROM_ROOT
)
2611 #define bottom_pattern
2612 #include "elfgcchack.h"
2613 #endif /* LIBXML_PATTERN_ENABLED */