Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / libxml / src / pattern.c
blob0e38c2dd21fe63e2159ca86fbbb4ed498ed9e753
1 /*
2 * pattern.c: Implemetation of selectors for nodes
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
9 * See Copyright for the status of this software.
11 * daniel@veillard.com
15 * TODO:
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
26 #define IN_LIBXML
27 #include "libxml.h"
29 #include <string.h>
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 | \
69 XML_PATTERN_XSSEL | \
70 XML_PATTERN_XSFIELD)
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 */
102 int flags;
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 */
113 int blockLevel;
116 static void xmlFreeStreamComp(xmlStreamCompPtr comp);
119 * Types are private:
122 typedef enum {
123 XML_OP_END=0,
124 XML_OP_ROOT,
125 XML_OP_ELEM,
126 XML_OP_CHILD,
127 XML_OP_ATTR,
128 XML_OP_PARENT,
129 XML_OP_ANCESTOR,
130 XML_OP_NS,
131 XML_OP_ALL
132 } xmlPatOp;
135 typedef struct _xmlStepState xmlStepState;
136 typedef xmlStepState *xmlStepStatePtr;
137 struct _xmlStepState {
138 int step;
139 xmlNodePtr node;
142 typedef struct _xmlStepStates xmlStepStates;
143 typedef xmlStepStates *xmlStepStatesPtr;
144 struct _xmlStepStates {
145 int nbstates;
146 int maxstates;
147 xmlStepStatePtr states;
150 typedef struct _xmlStepOp xmlStepOp;
151 typedef xmlStepOp *xmlStepOpPtr;
152 struct _xmlStepOp {
153 xmlPatOp op;
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)
161 struct _xmlPattern {
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 */
167 int nbStep;
168 int maxStep;
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 /************************************************************************
188 * Type functions *
190 ************************************************************************/
193 * xmlNewPattern:
195 * Create a new XSLT Pattern
197 * Returns the newly allocated xmlPatternPtr or NULL in case of error
199 static xmlPatternPtr
200 xmlNewPattern(void) {
201 xmlPatternPtr cur;
203 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
204 if (cur == NULL) {
205 ERROR(NULL, NULL, NULL,
206 "xmlNewPattern : malloc failed\n");
207 return(NULL);
209 memset(cur, 0, sizeof(xmlPattern));
210 cur->maxStep = 10;
211 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
212 if (cur->steps == NULL) {
213 xmlFree(cur);
214 ERROR(NULL, NULL, NULL,
215 "xmlNewPattern : malloc failed\n");
216 return(NULL);
218 return(cur);
222 * xmlFreePattern:
223 * @comp: an XSLT comp
225 * Free up the memory allocated by @comp
227 void
228 xmlFreePattern(xmlPatternPtr comp) {
229 xmlStepOpPtr op;
230 int i;
232 if (comp == NULL)
233 return;
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));
256 xmlFree(comp);
260 * xmlFreePatternList:
261 * @comp: an XSLT comp list
263 * Free up the memory allocated by all the elements of @comp
265 void
266 xmlFreePatternList(xmlPatternPtr comp) {
267 xmlPatternPtr cur;
269 while (comp != NULL) {
270 cur = comp;
271 comp = comp->next;
272 cur->next = NULL;
273 xmlFreePattern(cur);
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;
293 if (pattern == NULL)
294 return(NULL);
296 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
297 if (cur == NULL) {
298 ERROR(NULL, NULL, NULL,
299 "xmlNewPatParserContext : malloc failed\n");
300 return(NULL);
302 memset(cur, 0, sizeof(xmlPatParserContext));
303 cur->dict = dict;
304 cur->cur = pattern;
305 cur->base = pattern;
306 if (namespaces != NULL) {
307 int i;
308 for (i = 0;namespaces[2 * i] != NULL;i++);
309 cur->nb_namespaces = i;
310 } else {
311 cur->nb_namespaces = 0;
313 cur->namespaces = namespaces;
314 return(cur);
318 * xmlFreePatParserContext:
319 * @ctxt: an XSLT parser context
321 * Free up the memory allocated by @ctxt
323 static void
324 xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
325 if (ctxt == NULL)
326 return;
327 memset(ctxt, -1, sizeof(xmlPatParserContext));
328 xmlFree(ctxt);
332 * xmlPatternAdd:
333 * @comp: the compiled match expression
334 * @op: an op
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.
342 static int
343 xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
344 xmlPatternPtr comp,
345 xmlPatOp op, xmlChar * value, xmlChar * value2)
347 if (comp->nbStep >= comp->maxStep) {
348 xmlStepOpPtr temp;
349 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
350 sizeof(xmlStepOp));
351 if (temp == NULL) {
352 ERROR(ctxt, NULL, NULL,
353 "xmlPatternAdd: realloc failed\n");
354 return (-1);
356 comp->steps = temp;
357 comp->maxStep *= 2;
359 comp->steps[comp->nbStep].op = op;
360 comp->steps[comp->nbStep].value = value;
361 comp->steps[comp->nbStep].value2 = value2;
362 comp->nbStep++;
363 return (0);
366 #if 0
368 * xsltSwapTopPattern:
369 * @comp: the compiled match expression
371 * reverse the two top steps.
373 static void
374 xsltSwapTopPattern(xmlPatternPtr comp) {
375 int i;
376 int j = comp->nbStep - 1;
378 if (j > 0) {
379 register const xmlChar *tmp;
380 register xmlPatOp op;
381 i = j - 1;
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;
393 #endif
396 * xmlReversePattern:
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.
403 static int
404 xmlReversePattern(xmlPatternPtr comp) {
405 int i, j;
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;
416 comp->nbStep--;
418 if (comp->nbStep >= comp->maxStep) {
419 xmlStepOpPtr temp;
420 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
421 sizeof(xmlStepOp));
422 if (temp == NULL) {
423 ERROR(ctxt, NULL, NULL,
424 "xmlReversePattern: realloc failed\n");
425 return (-1);
427 comp->steps = temp;
428 comp->maxStep *= 2;
430 i = 0;
431 j = comp->nbStep - 1;
432 while (j > i) {
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;
444 j--;
445 i++;
447 comp->steps[comp->nbStep].value = NULL;
448 comp->steps[comp->nbStep].value2 = NULL;
449 comp->steps[comp->nbStep++].op = XML_OP_END;
450 return(0);
453 /************************************************************************
455 * The interpreter for the precompiled patterns *
457 ************************************************************************/
459 static int
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) {
467 xmlStepState *tmp;
469 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
470 2 * states->maxstates * sizeof(xmlStepState));
471 if (tmp == NULL)
472 return(-1);
473 states->states = tmp;
474 states->maxstates *= 2;
476 states->states[states->nbstates].step = step;
477 states->states[states->nbstates++].node = node;
478 #if 0
479 fprintf(stderr, "Push: %d, %s\n", step, node->name);
480 #endif
481 return(0);
485 * xmlPatMatch:
486 * @comp: the precompiled pattern
487 * @node: a node
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
493 static int
494 xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
495 int i;
496 xmlStepOpPtr step;
497 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
499 if ((comp == NULL) || (node == NULL)) return(-1);
500 i = 0;
501 restart:
502 for (;i < comp->nbStep;i++) {
503 step = &comp->steps[i];
504 switch (step->op) {
505 case XML_OP_END:
506 goto found;
507 case XML_OP_ROOT:
508 if (node->type == XML_NAMESPACE_DECL)
509 goto rollback;
510 node = node->parent;
511 if ((node->type == XML_DOCUMENT_NODE) ||
512 #ifdef LIBXML_DOCB_ENABLED
513 (node->type == XML_DOCB_DOCUMENT_NODE) ||
514 #endif
515 (node->type == XML_HTML_DOCUMENT_NODE))
516 continue;
517 goto rollback;
518 case XML_OP_ELEM:
519 if (node->type != XML_ELEMENT_NODE)
520 goto rollback;
521 if (step->value == NULL)
522 continue;
523 if (step->value[0] != node->name[0])
524 goto rollback;
525 if (!xmlStrEqual(step->value, node->name))
526 goto rollback;
528 /* Namespace test */
529 if (node->ns == NULL) {
530 if (step->value2 != NULL)
531 goto rollback;
532 } else if (node->ns->href != NULL) {
533 if (step->value2 == NULL)
534 goto rollback;
535 if (!xmlStrEqual(step->value2, node->ns->href))
536 goto rollback;
538 continue;
539 case XML_OP_CHILD: {
540 xmlNodePtr lst;
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) &&
546 #endif
547 (node->type != XML_HTML_DOCUMENT_NODE))
548 goto rollback;
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)))
557 break;
558 lst = lst->next;
560 if (lst != NULL)
561 continue;
563 goto rollback;
565 case XML_OP_ATTR:
566 if (node->type != XML_ATTRIBUTE_NODE)
567 goto rollback;
568 if (step->value != NULL) {
569 if (step->value[0] != node->name[0])
570 goto rollback;
571 if (!xmlStrEqual(step->value, node->name))
572 goto rollback;
574 /* Namespace test */
575 if (node->ns == NULL) {
576 if (step->value2 != NULL)
577 goto rollback;
578 } else if (step->value2 != NULL) {
579 if (!xmlStrEqual(step->value2, node->ns->href))
580 goto rollback;
582 continue;
583 case XML_OP_PARENT:
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) ||
588 #endif
589 (node->type == XML_NAMESPACE_DECL))
590 goto rollback;
591 node = node->parent;
592 if (node == NULL)
593 goto rollback;
594 if (step->value == NULL)
595 continue;
596 if (step->value[0] != node->name[0])
597 goto rollback;
598 if (!xmlStrEqual(step->value, node->name))
599 goto rollback;
600 /* Namespace test */
601 if (node->ns == NULL) {
602 if (step->value2 != NULL)
603 goto rollback;
604 } else if (node->ns->href != NULL) {
605 if (step->value2 == NULL)
606 goto rollback;
607 if (!xmlStrEqual(step->value2, node->ns->href))
608 goto rollback;
610 continue;
611 case XML_OP_ANCESTOR:
612 /* TODO: implement coalescing of ANCESTOR/NODE ops */
613 if (step->value == NULL) {
614 i++;
615 step = &comp->steps[i];
616 if (step->op == XML_OP_ROOT)
617 goto found;
618 if (step->op != XML_OP_ELEM)
619 goto rollback;
620 if (step->value == NULL)
621 return(-1);
623 if (node == NULL)
624 goto rollback;
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) ||
629 #endif
630 (node->type == XML_NAMESPACE_DECL))
631 goto rollback;
632 node = node->parent;
633 while (node != NULL) {
634 if ((node->type == XML_ELEMENT_NODE) &&
635 (step->value[0] == node->name[0]) &&
636 (xmlStrEqual(step->value, node->name))) {
637 /* Namespace test */
638 if (node->ns == NULL) {
639 if (step->value2 == NULL)
640 break;
641 } else if (node->ns->href != NULL) {
642 if ((step->value2 != NULL) &&
643 (xmlStrEqual(step->value2, node->ns->href)))
644 break;
647 node = node->parent;
649 if (node == NULL)
650 goto rollback;
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);
657 else
658 xmlPatPushState(&states, i - 1, node);
659 continue;
660 case XML_OP_NS:
661 if (node->type != XML_ELEMENT_NODE)
662 goto rollback;
663 if (node->ns == NULL) {
664 if (step->value != NULL)
665 goto rollback;
666 } else if (node->ns->href != NULL) {
667 if (step->value == NULL)
668 goto rollback;
669 if (!xmlStrEqual(step->value, node->ns->href))
670 goto rollback;
672 break;
673 case XML_OP_ALL:
674 if (node->type != XML_ELEMENT_NODE)
675 goto rollback;
676 break;
679 found:
680 if (states.states != NULL) {
681 /* Free the rollback states */
682 xmlFree(states.states);
684 return(1);
685 rollback:
686 /* got an error try to rollback */
687 if (states.states == NULL)
688 return(0);
689 if (states.nbstates <= 0) {
690 xmlFree(states.states);
691 return(0);
693 states.nbstates--;
694 i = states.states[states.nbstates].step;
695 node = states.states[states.nbstates].node;
696 #if 0
697 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
698 #endif
699 goto restart;
702 /************************************************************************
704 * Dedicated parser for templates *
706 ************************************************************************/
708 #define TODO \
709 xmlGenericError(xmlGenericErrorContext, \
710 "Unimplemented block at %s:%d\n", \
711 __FILE__, __LINE__);
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); }
736 #if 0
738 * xmlPatScanLiteral:
739 * @ctxt: the XPath Parser context
741 * Parse an XPath Litteral:
743 * [29] Literal ::= '"' [^"]* '"'
744 * | "'" [^']* "'"
746 * Returns the Literal parsed or NULL
749 static xmlChar *
750 xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
751 const xmlChar *q, *cur;
752 xmlChar *ret = NULL;
753 int val, len;
755 SKIP_BLANKS;
756 if (CUR == '"') {
757 NEXT;
758 cur = q = CUR_PTR;
759 val = xmlStringCurrentChar(NULL, cur, &len);
760 while ((IS_CHAR(val)) && (val != '"')) {
761 cur += len;
762 val = xmlStringCurrentChar(NULL, cur, &len);
764 if (!IS_CHAR(val)) {
765 ctxt->error = 1;
766 return(NULL);
767 } else {
768 if (ctxt->dict)
769 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
770 else
771 ret = xmlStrndup(q, cur - q);
773 cur += len;
774 CUR_PTR = cur;
775 } else if (CUR == '\'') {
776 NEXT;
777 cur = q = CUR_PTR;
778 val = xmlStringCurrentChar(NULL, cur, &len);
779 while ((IS_CHAR(val)) && (val != '\'')) {
780 cur += len;
781 val = xmlStringCurrentChar(NULL, cur, &len);
783 if (!IS_CHAR(val)) {
784 ctxt->error = 1;
785 return(NULL);
786 } else {
787 if (ctxt->dict)
788 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
789 else
790 ret = xmlStrndup(q, cur - q);
792 cur += len;
793 CUR_PTR = cur;
794 } else {
795 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
796 ctxt->error = 1;
797 return(NULL);
799 return(ret);
801 #endif
804 * xmlPatScanName:
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
817 static xmlChar *
818 xmlPatScanName(xmlPatParserContextPtr ctxt) {
819 const xmlChar *q, *cur;
820 xmlChar *ret = NULL;
821 int val, len;
823 SKIP_BLANKS;
825 cur = q = CUR_PTR;
826 val = xmlStringCurrentChar(NULL, cur, &len);
827 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
828 return(NULL);
830 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
831 (val == '.') || (val == '-') ||
832 (val == '_') ||
833 (IS_COMBINING(val)) ||
834 (IS_EXTENDER(val))) {
835 cur += len;
836 val = xmlStringCurrentChar(NULL, cur, &len);
838 if (ctxt->dict)
839 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
840 else
841 ret = xmlStrndup(q, cur - q);
842 CUR_PTR = cur;
843 return(ret);
847 * xmlPatScanNCName:
848 * @ctxt: the XPath Parser context
850 * Parses a non qualified name
852 * Returns the Name parsed or NULL
855 static xmlChar *
856 xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
857 const xmlChar *q, *cur;
858 xmlChar *ret = NULL;
859 int val, len;
861 SKIP_BLANKS;
863 cur = q = CUR_PTR;
864 val = xmlStringCurrentChar(NULL, cur, &len);
865 if (!IS_LETTER(val) && (val != '_'))
866 return(NULL);
868 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
869 (val == '.') || (val == '-') ||
870 (val == '_') ||
871 (IS_COMBINING(val)) ||
872 (IS_EXTENDER(val))) {
873 cur += len;
874 val = xmlStringCurrentChar(NULL, cur, &len);
876 if (ctxt->dict)
877 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
878 else
879 ret = xmlStrndup(q, cur - q);
880 CUR_PTR = cur;
881 return(ret);
884 #if 0
886 * xmlPatScanQName:
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
895 static xmlChar *
896 xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
897 xmlChar *ret = NULL;
899 *prefix = NULL;
900 ret = xmlPatScanNCName(ctxt);
901 if (CUR == ':') {
902 *prefix = ret;
903 NEXT;
904 ret = xmlPatScanNCName(ctxt);
906 return(ret);
908 #endif
911 * xmlCompileAttributeTest:
912 * @ctxt: the compilation context
914 * Compile an attribute test.
916 static void
917 xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
918 xmlChar *token = NULL;
919 xmlChar *name = NULL;
920 xmlChar *URL = NULL;
922 SKIP_BLANKS;
923 name = xmlPatScanNCName(ctxt);
924 if (name == NULL) {
925 if (CUR == '*') {
926 PUSH(XML_OP_ATTR, NULL, NULL);
927 NEXT;
928 } else {
929 ERROR(NULL, NULL, NULL,
930 "xmlCompileAttributeTest : Name expected\n");
931 ctxt->error = 1;
933 return;
935 if (CUR == ':') {
936 int i;
937 xmlChar *prefix = name;
939 NEXT;
941 if (IS_BLANK_CH(CUR)) {
942 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
943 XML_PAT_FREE_STRING(ctxt, prefix);
944 ctxt->error = 1;
945 goto error;
948 * This is a namespace match
950 token = xmlPatScanName(ctxt);
951 if ((prefix[0] == 'x') &&
952 (prefix[1] == 'm') &&
953 (prefix[2] == 'l') &&
954 (prefix[3] == 0))
956 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
957 } else {
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])
961 break;
964 if (i >= ctxt->nb_namespaces) {
965 ERROR5(NULL, NULL, NULL,
966 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
967 prefix);
968 ctxt->error = 1;
969 goto error;
972 XML_PAT_FREE_STRING(ctxt, prefix);
973 if (token == NULL) {
974 if (CUR == '*') {
975 NEXT;
976 PUSH(XML_OP_ATTR, NULL, URL);
977 } else {
978 ERROR(NULL, NULL, NULL,
979 "xmlCompileAttributeTest : Name expected\n");
980 ctxt->error = 1;
981 goto error;
983 } else {
984 PUSH(XML_OP_ATTR, token, URL);
986 } else {
987 PUSH(XML_OP_ATTR, name, NULL);
989 return;
990 error:
991 if (URL != NULL)
992 XML_PAT_FREE_STRING(ctxt, URL)
993 if (token != NULL)
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 ':' '*'
1008 static void
1009 xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1010 xmlChar *token = NULL;
1011 xmlChar *name = NULL;
1012 xmlChar *URL = NULL;
1013 int hasBlanks = 0;
1015 SKIP_BLANKS;
1016 if (CUR == '.') {
1018 * Context node.
1020 NEXT;
1021 PUSH(XML_OP_ELEM, NULL, NULL);
1022 return;
1024 if (CUR == '@') {
1026 * Attribute test.
1028 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1029 ERROR5(NULL, NULL, NULL,
1030 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1031 ctxt->error = 1;
1032 return;
1034 NEXT;
1035 xmlCompileAttributeTest(ctxt);
1036 if (ctxt->error != 0)
1037 goto error;
1038 return;
1040 name = xmlPatScanNCName(ctxt);
1041 if (name == NULL) {
1042 if (CUR == '*') {
1043 NEXT;
1044 PUSH(XML_OP_ALL, NULL, NULL);
1045 return;
1046 } else {
1047 ERROR(NULL, NULL, NULL,
1048 "xmlCompileStepPattern : Name expected\n");
1049 ctxt->error = 1;
1050 return;
1053 if (IS_BLANK_CH(CUR)) {
1054 hasBlanks = 1;
1055 SKIP_BLANKS;
1057 if (CUR == ':') {
1058 NEXT;
1059 if (CUR != ':') {
1060 xmlChar *prefix = name;
1061 int i;
1063 if (hasBlanks || IS_BLANK_CH(CUR)) {
1064 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1065 ctxt->error = 1;
1066 goto error;
1069 * This is a namespace match
1071 token = xmlPatScanName(ctxt);
1072 if ((prefix[0] == 'x') &&
1073 (prefix[1] == 'm') &&
1074 (prefix[2] == 'l') &&
1075 (prefix[3] == 0))
1077 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1078 } else {
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])
1082 break;
1085 if (i >= ctxt->nb_namespaces) {
1086 ERROR5(NULL, NULL, NULL,
1087 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1088 prefix);
1089 ctxt->error = 1;
1090 goto error;
1093 XML_PAT_FREE_STRING(ctxt, prefix);
1094 name = NULL;
1095 if (token == NULL) {
1096 if (CUR == '*') {
1097 NEXT;
1098 PUSH(XML_OP_NS, URL, NULL);
1099 } else {
1100 ERROR(NULL, NULL, NULL,
1101 "xmlCompileStepPattern : Name expected\n");
1102 ctxt->error = 1;
1103 goto error;
1105 } else {
1106 PUSH(XML_OP_ELEM, token, URL);
1108 } else {
1109 NEXT;
1110 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1111 XML_PAT_FREE_STRING(ctxt, name);
1112 name = xmlPatScanName(ctxt);
1113 if (name == NULL) {
1114 if (CUR == '*') {
1115 NEXT;
1116 PUSH(XML_OP_ALL, NULL, NULL);
1117 return;
1118 } else {
1119 ERROR(NULL, NULL, NULL,
1120 "xmlCompileStepPattern : QName expected\n");
1121 ctxt->error = 1;
1122 goto error;
1125 if (CUR == ':') {
1126 xmlChar *prefix = name;
1127 int i;
1129 NEXT;
1130 if (IS_BLANK_CH(CUR)) {
1131 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1132 ctxt->error = 1;
1133 goto error;
1136 * This is a namespace match
1138 token = xmlPatScanName(ctxt);
1139 if ((prefix[0] == 'x') &&
1140 (prefix[1] == 'm') &&
1141 (prefix[2] == 'l') &&
1142 (prefix[3] == 0))
1144 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1145 } else {
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])
1149 break;
1152 if (i >= ctxt->nb_namespaces) {
1153 ERROR5(NULL, NULL, NULL,
1154 "xmlCompileStepPattern : no namespace bound "
1155 "to prefix %s\n", prefix);
1156 ctxt->error = 1;
1157 goto error;
1160 XML_PAT_FREE_STRING(ctxt, prefix);
1161 name = NULL;
1162 if (token == NULL) {
1163 if (CUR == '*') {
1164 NEXT;
1165 PUSH(XML_OP_NS, URL, NULL);
1166 } else {
1167 ERROR(NULL, NULL, NULL,
1168 "xmlCompileStepPattern : Name expected\n");
1169 ctxt->error = 1;
1170 goto error;
1172 } else {
1173 PUSH(XML_OP_CHILD, token, URL);
1175 } else
1176 PUSH(XML_OP_CHILD, name, NULL);
1177 return;
1178 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1179 XML_PAT_FREE_STRING(ctxt, name)
1180 name = NULL;
1181 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1182 ERROR5(NULL, NULL, NULL,
1183 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1184 ctxt->error = 1;
1185 goto error;
1187 xmlCompileAttributeTest(ctxt);
1188 if (ctxt->error != 0)
1189 goto error;
1190 return;
1191 } else {
1192 ERROR5(NULL, NULL, NULL,
1193 "The 'element' or 'attribute' axis is expected.\n", NULL);
1194 ctxt->error = 1;
1195 goto error;
1198 } else if (CUR == '*') {
1199 if (name != NULL) {
1200 ctxt->error = 1;
1201 goto error;
1203 NEXT;
1204 PUSH(XML_OP_ALL, token, NULL);
1205 } else {
1206 PUSH(XML_OP_ELEM, name, NULL);
1208 return;
1209 error:
1210 if (URL != NULL)
1211 XML_PAT_FREE_STRING(ctxt, URL)
1212 if (token != NULL)
1213 XML_PAT_FREE_STRING(ctxt, token)
1214 if (name != NULL)
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 )
1227 static void
1228 xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1229 SKIP_BLANKS;
1230 if (CUR == '/') {
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);
1238 NEXT;
1239 NEXT;
1240 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1241 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1242 NEXT;
1243 NEXT;
1244 NEXT;
1245 /* Check for incompleteness. */
1246 SKIP_BLANKS;
1247 if (CUR == 0) {
1248 ERROR5(NULL, NULL, NULL,
1249 "Incomplete expression '%s'.\n", ctxt->base);
1250 ctxt->error = 1;
1251 goto error;
1254 if (CUR == '@') {
1255 NEXT;
1256 xmlCompileAttributeTest(ctxt);
1257 SKIP_BLANKS;
1258 /* TODO: check for incompleteness */
1259 if (CUR != 0) {
1260 xmlCompileStepPattern(ctxt);
1261 if (ctxt->error != 0)
1262 goto error;
1264 } else {
1265 if (CUR == '/') {
1266 PUSH(XML_OP_ROOT, NULL, NULL);
1267 NEXT;
1268 /* Check for incompleteness. */
1269 SKIP_BLANKS;
1270 if (CUR == 0) {
1271 ERROR5(NULL, NULL, NULL,
1272 "Incomplete expression '%s'.\n", ctxt->base);
1273 ctxt->error = 1;
1274 goto error;
1277 xmlCompileStepPattern(ctxt);
1278 if (ctxt->error != 0)
1279 goto error;
1280 SKIP_BLANKS;
1281 while (CUR == '/') {
1282 if (NXT(1) == '/') {
1283 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1284 NEXT;
1285 NEXT;
1286 SKIP_BLANKS;
1287 xmlCompileStepPattern(ctxt);
1288 if (ctxt->error != 0)
1289 goto error;
1290 } else {
1291 PUSH(XML_OP_PARENT, NULL, NULL);
1292 NEXT;
1293 SKIP_BLANKS;
1294 if (CUR == 0) {
1295 ERROR5(NULL, NULL, NULL,
1296 "Incomplete expression '%s'.\n", ctxt->base);
1297 ctxt->error = 1;
1298 goto error;
1300 xmlCompileStepPattern(ctxt);
1301 if (ctxt->error != 0)
1302 goto error;
1306 if (CUR != 0) {
1307 ERROR5(NULL, NULL, NULL,
1308 "Failed to compile pattern %s\n", ctxt->base);
1309 ctxt->error = 1;
1311 error:
1312 return;
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 )
1324 static void
1325 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1326 SKIP_BLANKS;
1327 if (CUR == '/') {
1328 ERROR5(NULL, NULL, NULL,
1329 "Unexpected selection of the document root in '%s'.\n",
1330 ctxt->base);
1331 goto error;
1333 ctxt->comp->flags |= PAT_FROM_CUR;
1335 if (CUR == '.') {
1336 /* "." - "self::node()" */
1337 NEXT;
1338 SKIP_BLANKS;
1339 if (CUR == 0) {
1341 * Selection of the context node.
1343 PUSH(XML_OP_ELEM, NULL, NULL);
1344 return;
1346 if (CUR != '/') {
1347 /* TODO: A more meaningful error message. */
1348 ERROR5(NULL, NULL, NULL,
1349 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1350 goto error;
1352 /* "./" - "self::node()/" */
1353 NEXT;
1354 SKIP_BLANKS;
1355 if (CUR == '/') {
1356 if (IS_BLANK_CH(PEEKPREV(1))) {
1358 * Disallow "./ /"
1360 ERROR5(NULL, NULL, NULL,
1361 "Unexpected '/' token in '%s'.\n", ctxt->base);
1362 goto error;
1364 /* ".//" - "self:node()/descendant-or-self::node()/" */
1365 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1366 NEXT;
1367 SKIP_BLANKS;
1369 if (CUR == 0)
1370 goto error_unfinished;
1373 * Process steps.
1375 do {
1376 xmlCompileStepPattern(ctxt);
1377 if (ctxt->error != 0)
1378 goto error;
1379 SKIP_BLANKS;
1380 if (CUR != '/')
1381 break;
1382 PUSH(XML_OP_PARENT, NULL, NULL);
1383 NEXT;
1384 SKIP_BLANKS;
1385 if (CUR == '/') {
1387 * Disallow subsequent '//'.
1389 ERROR5(NULL, NULL, NULL,
1390 "Unexpected subsequent '//' in '%s'.\n",
1391 ctxt->base);
1392 goto error;
1394 if (CUR == 0)
1395 goto error_unfinished;
1397 } while (CUR != 0);
1399 if (CUR != 0) {
1400 ERROR5(NULL, NULL, NULL,
1401 "Failed to compile expression '%s'.\n", ctxt->base);
1402 ctxt->error = 1;
1404 return;
1405 error:
1406 ctxt->error = 1;
1407 return;
1409 error_unfinished:
1410 ctxt->error = 1;
1411 ERROR5(NULL, NULL, NULL,
1412 "Unfinished expression '%s'.\n", ctxt->base);
1413 return;
1416 /************************************************************************
1418 * The streaming code *
1420 ************************************************************************/
1422 #ifdef DEBUG_STREAMING
1423 static void
1424 xmlDebugStreamComp(xmlStreamCompPtr stream) {
1425 int i;
1427 if (stream == NULL) {
1428 printf("Stream: NULL\n");
1429 return;
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) {
1437 printf("* ");
1438 } else {
1439 printf("%s ", stream->steps[i].name);
1441 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1442 printf("root ");
1443 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1444 printf("// ");
1445 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1446 printf("final ");
1447 printf("\n");
1450 static void
1451 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1452 int i;
1454 if (ctxt == NULL) {
1455 printf("Stream: NULL\n");
1456 return;
1458 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1459 if (match)
1460 printf("matches\n");
1461 else
1462 printf("\n");
1463 for (i = 0;i < ctxt->nbState;i++) {
1464 if (ctxt->states[2 * i] < 0)
1465 printf(" %d: free\n", i);
1466 else {
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)
1471 printf(" //\n");
1472 else
1473 printf("\n");
1477 #endif
1479 * xmlNewStreamComp:
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;
1490 if (size < 4)
1491 size = 4;
1493 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1494 if (cur == NULL) {
1495 ERROR(NULL, NULL, NULL,
1496 "xmlNewStreamComp: malloc failed\n");
1497 return(NULL);
1499 memset(cur, 0, sizeof(xmlStreamComp));
1500 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1501 if (cur->steps == NULL) {
1502 xmlFree(cur);
1503 ERROR(NULL, NULL, NULL,
1504 "xmlNewStreamComp: malloc failed\n");
1505 return(NULL);
1507 cur->nbStep = 0;
1508 cur->maxStep = size;
1509 return(cur);
1513 * xmlFreeStreamComp:
1514 * @comp: the compiled pattern for streaming
1516 * Free the compiled pattern for streaming
1518 static void
1519 xmlFreeStreamComp(xmlStreamCompPtr comp) {
1520 if (comp != NULL) {
1521 if (comp->steps != NULL)
1522 xmlFree(comp->steps);
1523 if (comp->dict != NULL)
1524 xmlDictFree(comp->dict);
1525 xmlFree(comp);
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
1540 static int
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));
1548 if (cur == NULL) {
1549 ERROR(NULL, NULL, NULL,
1550 "xmlNewStreamComp: malloc failed\n");
1551 return(-1);
1553 comp->steps = cur;
1554 comp->maxStep *= 2;
1556 cur = &comp->steps[comp->nbStep++];
1557 cur->flags = flags;
1558 cur->name = name;
1559 cur->ns = ns;
1560 cur->nodeType = nodeType;
1561 return(comp->nbStep - 1);
1565 * xmlStreamCompile:
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.
1572 static int
1573 xmlStreamCompile(xmlPatternPtr comp) {
1574 xmlStreamCompPtr stream;
1575 int i, s = 0, root = 0, flags = 0, prevs = -1;
1576 xmlStepOp step;
1578 if ((comp == NULL) || (comp->steps == NULL))
1579 return(-1);
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);
1588 if (stream == NULL)
1589 return(-1);
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;
1593 return(0);
1596 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1597 if (stream == NULL)
1598 return(-1);
1599 if (comp->dict != NULL) {
1600 stream->dict = comp->dict;
1601 xmlDictReference(stream->dict);
1604 i = 0;
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];
1610 switch (step.op) {
1611 case XML_OP_END:
1612 break;
1613 case XML_OP_ROOT:
1614 if (i != 0)
1615 goto error;
1616 root = 1;
1617 break;
1618 case XML_OP_NS:
1619 s = xmlStreamCompAddStep(stream, NULL, step.value,
1620 XML_ELEMENT_NODE, flags);
1621 if (s < 0)
1622 goto error;
1623 prevs = s;
1624 flags = 0;
1625 break;
1626 case XML_OP_ATTR:
1627 flags |= XML_STREAM_STEP_ATTR;
1628 prevs = -1;
1629 s = xmlStreamCompAddStep(stream,
1630 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1631 flags = 0;
1632 if (s < 0)
1633 goto error;
1634 break;
1635 case XML_OP_ELEM:
1636 if ((step.value == NULL) && (step.value2 == NULL)) {
1638 * We have a "." or "self::node()" here.
1639 * Eliminate redundant self::node() tests like in "/./."
1640 * or "//./"
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);
1657 if (s < 0)
1658 goto error;
1659 flags = 0;
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.
1666 if (prevs != -1) {
1667 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1668 prevs = -1;
1670 break;
1672 } else {
1673 /* Just skip this one. */
1674 continue;
1677 /* An element node. */
1678 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1679 XML_ELEMENT_NODE, flags);
1680 if (s < 0)
1681 goto error;
1682 prevs = s;
1683 flags = 0;
1684 break;
1685 case XML_OP_CHILD:
1686 /* An element node child. */
1687 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1688 XML_ELEMENT_NODE, flags);
1689 if (s < 0)
1690 goto error;
1691 prevs = s;
1692 flags = 0;
1693 break;
1694 case XML_OP_ALL:
1695 s = xmlStreamCompAddStep(stream, NULL, NULL,
1696 XML_ELEMENT_NODE, flags);
1697 if (s < 0)
1698 goto error;
1699 prevs = s;
1700 flags = 0;
1701 break;
1702 case XML_OP_PARENT:
1703 break;
1704 case XML_OP_ANCESTOR:
1705 /* Skip redundant continuations. */
1706 if (flags & XML_STREAM_STEP_DESC)
1707 break;
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;
1714 break;
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
1721 * tree level.
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)
1732 goto error;
1733 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1734 if (root)
1735 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1736 #ifdef DEBUG_STREAMING
1737 xmlDebugStreamComp(stream);
1738 #endif
1739 comp->stream = stream;
1740 return(0);
1741 error:
1742 xmlFreeStreamComp(stream);
1743 return(0);
1747 * xmlNewStreamCtxt:
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));
1759 if (cur == NULL) {
1760 ERROR(NULL, NULL, NULL,
1761 "xmlNewStreamCtxt: malloc failed\n");
1762 return(NULL);
1764 memset(cur, 0, sizeof(xmlStreamCtxt));
1765 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1766 if (cur->states == NULL) {
1767 xmlFree(cur);
1768 ERROR(NULL, NULL, NULL,
1769 "xmlNewStreamCtxt: malloc failed\n");
1770 return(NULL);
1772 cur->nbState = 0;
1773 cur->maxState = 4;
1774 cur->level = 0;
1775 cur->comp = stream;
1776 cur->blockLevel = -1;
1777 return(cur);
1781 * xmlFreeStreamCtxt:
1782 * @stream: the stream context
1784 * Free the stream context
1786 void
1787 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1788 xmlStreamCtxtPtr next;
1790 while (stream != NULL) {
1791 next = stream->next;
1792 if (stream->states != NULL)
1793 xmlFree(stream->states);
1794 xmlFree(stream);
1795 stream = next;
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
1808 static int
1809 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1810 int i;
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;
1815 return(i);
1818 if (comp->nbState >= comp->maxState) {
1819 int *cur;
1821 cur = (int *) xmlRealloc(comp->states,
1822 comp->maxState * 4 * sizeof(int));
1823 if (cur == NULL) {
1824 ERROR(NULL, NULL, NULL,
1825 "xmlNewStreamCtxt: malloc failed\n");
1826 return(-1);
1828 comp->states = cur;
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.
1852 static int
1853 xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1854 const xmlChar *name, const xmlChar *ns,
1855 int nodeType) {
1856 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1857 xmlStreamCompPtr comp;
1858 xmlStreamStep step;
1859 #ifdef DEBUG_STREAMING
1860 xmlStreamCtxtPtr orig = stream;
1861 #endif
1863 if ((stream == NULL) || (stream->nbState < 0))
1864 return(-1);
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;
1873 stream->level = 0;
1874 stream->blockLevel = -1;
1875 if (comp->flags & XML_STREAM_FROM_ROOT) {
1876 if (comp->nbStep == 0) {
1877 /* TODO: We have a "/." here? */
1878 ret = 1;
1879 } else {
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
1886 * as well.
1888 ret = 1;
1889 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1890 /* TODO: Do we need this ? */
1891 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1892 if (tmp < 0)
1893 err++;
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))) {
1921 ret = 1;
1923 stream->level++;
1924 goto stream_next;
1926 if (stream->blockLevel != -1) {
1928 * Skip blocked expressions.
1930 stream->level++;
1931 goto stream_next;
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?
1942 stream->level++;
1943 goto stream_next;
1947 * Check evolution of existing states
1949 i = 0;
1950 m = stream->nbState;
1951 while (i < m) {
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] <
1962 stream->level) {
1963 return (-1);
1965 desc = 0;
1966 /* loop-stopper */
1967 i = m;
1968 } else {
1970 * If there are "//", then we need to process every "//"
1971 * occuring in the states, plus any other state for this
1972 * level.
1974 stepNr = stream->states[2 * i];
1976 /* TODO: should not happen anymore: dead states */
1977 if (stepNr < 0)
1978 goto next_state;
1980 tmp = stream->states[(2 * i) + 1];
1982 /* skip new states just added */
1983 if (tmp > stream->level)
1984 goto next_state;
1986 /* skip states at ancestor levels, except if "//" */
1987 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1988 if ((tmp < stream->level) && (!desc))
1989 goto next_state;
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;
2002 goto next_state;
2003 } else if (step.nodeType != XML_STREAM_ANY_NODE)
2004 goto next_state;
2007 * Compare local/namespace-name.
2009 match = 0;
2010 if (step.nodeType == XML_STREAM_ANY_NODE) {
2011 match = 1;
2012 } else if (step.name == NULL) {
2013 if (step.ns == NULL) {
2015 * This lets through all elements/attributes.
2017 match = 1;
2018 } else if (ns != NULL)
2019 match = xmlStrEqual(step.ns, ns);
2020 } else if (((step.ns != NULL) == (ns != NULL)) &&
2021 (name != NULL) &&
2022 (step.name[0] == name[0]) &&
2023 xmlStrEqual(step.name, name) &&
2024 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2026 match = 1;
2028 #if 0
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)
2037 match = 1;
2038 else
2039 match = (step.ns == ns);
2040 } else {
2041 match = ((step.name == name) && (step.ns == ns));
2043 #endif /* if 0 ------------------------------------------------------- */
2044 if (match) {
2045 final = step.flags & XML_STREAM_STEP_FINAL;
2046 if (desc) {
2047 if (final) {
2048 ret = 1;
2049 } else {
2050 /* descending match create a new state */
2051 xmlStreamCtxtAddState(stream, stepNr + 1,
2052 stream->level + 1);
2054 } else {
2055 if (final) {
2056 ret = 1;
2057 } else {
2058 xmlStreamCtxtAddState(stream, stepNr + 1,
2059 stream->level + 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.
2067 ret = 1;
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;
2079 next_state:
2080 i++;
2083 stream->level++;
2086 * Re/enter the expression.
2087 * Don't reenter if it's an absolute expression like "/foo",
2088 * except "//foo".
2090 step = comp->steps[0];
2091 if (step.flags & XML_STREAM_STEP_ROOT)
2092 goto stream_next;
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.
2107 goto stream_next;
2108 } else
2109 goto compare;
2112 * A "//" is always reentrant.
2114 if (desc)
2115 goto compare;
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))
2123 goto compare;
2125 goto stream_next;
2128 compare:
2130 * Check expected node-type.
2132 if (step.nodeType != nodeType) {
2133 if (nodeType == XML_ATTRIBUTE_NODE)
2134 goto stream_next;
2135 else if (step.nodeType != XML_STREAM_ANY_NODE)
2136 goto stream_next;
2139 * Compare local/namespace-name.
2141 match = 0;
2142 if (step.nodeType == XML_STREAM_ANY_NODE) {
2143 match = 1;
2144 } else if (step.name == NULL) {
2145 if (step.ns == NULL) {
2147 * This lets through all elements/attributes.
2149 match = 1;
2150 } else if (ns != NULL)
2151 match = xmlStrEqual(step.ns, ns);
2152 } else if (((step.ns != NULL) == (ns != NULL)) &&
2153 (name != NULL) &&
2154 (step.name[0] == name[0]) &&
2155 xmlStrEqual(step.name, name) &&
2156 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2158 match = 1;
2160 final = step.flags & XML_STREAM_STEP_FINAL;
2161 if (match) {
2162 if (final)
2163 ret = 1;
2164 else
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.
2171 ret = 1;
2174 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2175 ((! match) || final)) {
2177 * Mark this expression as blocked for any evaluation at
2178 * deeper levels.
2180 stream->blockLevel = stream->level;
2183 stream_next:
2184 stream = stream->next;
2185 } /* while stream != NULL */
2187 if (err > 0)
2188 ret = -1;
2189 #ifdef DEBUG_STREAMING
2190 xmlDebugStreamCtxt(orig, ret);
2191 #endif
2192 return(ret);
2196 * xmlStreamPush:
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,
2239 int nodeType)
2241 return (xmlStreamPushInternal(stream, name, ns,
2242 nodeType));
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));
2268 * xmlStreamPop:
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) {
2277 int i, lev;
2279 if (stream == NULL)
2280 return(-1);
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)
2293 if (stream->level)
2294 stream->level--;
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)
2302 stream->nbState--;
2303 if (lev <= stream->level)
2304 break;
2306 stream = stream->next;
2308 return(0);
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)
2327 return(-1);
2328 while (streamCtxt != NULL) {
2329 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2330 return(1);
2331 streamCtxt = streamCtxt->next;
2333 return(0);
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
2353 xmlPatternPtr
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;
2360 int type = 0;
2361 int streamable = 1;
2363 if (pattern == NULL)
2364 return(NULL);
2366 start = pattern;
2367 or = start;
2368 while (*or != 0) {
2369 tmp = NULL;
2370 while ((*or != 0) && (*or != '|')) or++;
2371 if (*or == 0)
2372 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2373 else {
2374 tmp = xmlStrndup(start, or - start);
2375 if (tmp != NULL) {
2376 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2378 or++;
2380 if (ctxt == NULL) goto error;
2381 cur = xmlNewPattern();
2382 if (cur == NULL) goto error;
2384 * Assign string dict.
2386 if (dict) {
2387 cur->dict = dict;
2388 xmlDictReference(dict);
2390 if (ret == NULL)
2391 ret = cur;
2392 else {
2393 cur->next = ret->next;
2394 ret->next = cur;
2396 cur->flags = flags;
2397 ctxt->comp = cur;
2399 if (XML_STREAM_XS_IDC(cur))
2400 xmlCompileIDCXPathPath(ctxt);
2401 else
2402 xmlCompilePathPattern(ctxt);
2403 if (ctxt->error != 0)
2404 goto error;
2405 xmlFreePatParserContext(ctxt);
2406 ctxt = NULL;
2409 if (streamable) {
2410 if (type == 0) {
2411 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2412 } else if (type == PAT_FROM_ROOT) {
2413 if (cur->flags & PAT_FROM_CUR)
2414 streamable = 0;
2415 } else if (type == PAT_FROM_CUR) {
2416 if (cur->flags & PAT_FROM_ROOT)
2417 streamable = 0;
2420 if (streamable)
2421 xmlStreamCompile(cur);
2422 if (xmlReversePattern(cur) < 0)
2423 goto error;
2424 if (tmp != NULL) {
2425 xmlFree(tmp);
2426 tmp = NULL;
2428 start = or;
2430 if (streamable == 0) {
2431 cur = ret;
2432 while (cur != NULL) {
2433 if (cur->stream != NULL) {
2434 xmlFreeStreamComp(cur->stream);
2435 cur->stream = NULL;
2437 cur = cur->next;
2441 return(ret);
2442 error:
2443 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2444 if (ret != NULL) xmlFreePattern(ret);
2445 if (tmp != NULL) xmlFree(tmp);
2446 return(NULL);
2450 * xmlPatternMatch:
2451 * @comp: the precompiled pattern
2452 * @node: a node
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)
2461 int ret = 0;
2463 if ((comp == NULL) || (node == NULL))
2464 return(-1);
2466 while (comp != NULL) {
2467 ret = xmlPatMatch(comp, node);
2468 if (ret != 0)
2469 return(ret);
2470 comp = comp->next;
2472 return(ret);
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
2484 xmlStreamCtxtPtr
2485 xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2487 xmlStreamCtxtPtr ret = NULL, cur;
2489 if ((comp == NULL) || (comp->stream == NULL))
2490 return(NULL);
2492 while (comp != NULL) {
2493 if (comp->stream == NULL)
2494 goto failed;
2495 cur = xmlNewStreamCtxt(comp->stream);
2496 if (cur == NULL)
2497 goto failed;
2498 if (ret == NULL)
2499 ret = cur;
2500 else {
2501 cur->next = ret->next;
2502 ret->next = cur;
2504 cur->flags = comp->flags;
2505 comp = comp->next;
2507 return(ret);
2508 failed:
2509 xmlFreeStreamCtxt(ret);
2510 return(NULL);
2514 * xmlPatternStreamable:
2515 * @comp: the precompiled pattern
2517 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2518 * should work.
2520 * Returns 1 if streamable, 0 if not and -1 in case of error.
2523 xmlPatternStreamable(xmlPatternPtr comp) {
2524 if (comp == NULL)
2525 return(-1);
2526 while (comp != NULL) {
2527 if (comp->stream == NULL)
2528 return(0);
2529 comp = comp->next;
2531 return(1);
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) {
2545 int ret = 0, i;
2546 if (comp == NULL)
2547 return(-1);
2548 while (comp != NULL) {
2549 if (comp->stream == NULL)
2550 return(-1);
2551 for (i = 0;i < comp->stream->nbStep;i++)
2552 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2553 return(-2);
2554 if (comp->stream->nbStep > ret)
2555 ret = comp->stream->nbStep;
2556 comp = comp->next;
2558 return(ret);
2562 * xmlPatternMinDepth:
2563 * @comp: the precompiled pattern
2565 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2566 * part of the set.
2568 * Returns -1 in case of error otherwise the depth,
2572 xmlPatternMinDepth(xmlPatternPtr comp) {
2573 int ret = 12345678;
2574 if (comp == NULL)
2575 return(-1);
2576 while (comp != NULL) {
2577 if (comp->stream == NULL)
2578 return(-1);
2579 if (comp->stream->nbStep < ret)
2580 ret = comp->stream->nbStep;
2581 if (ret == 0)
2582 return(0);
2583 comp = comp->next;
2585 return(ret);
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) {
2598 if (comp == NULL)
2599 return(-1);
2600 while (comp != NULL) {
2601 if (comp->stream == NULL)
2602 return(-1);
2603 if (comp->flags & PAT_FROM_ROOT)
2604 return(1);
2605 comp = comp->next;
2607 return(0);
2611 #define bottom_pattern
2612 #include "elfgcchack.h"
2613 #endif /* LIBXML_PATTERN_ENABLED */