VirtualBox

source: vbox/trunk/src/libs/libxml2-2.13.2/pattern.c@ 107044

Last change on this file since 107044 was 105420, checked in by vboxsync, 4 months ago

libxml2-2.12.6: Applied and adjusted our libxml2 changes to 2.12.6. bugref:10730

  • Property svn:eol-style set to native
File size: 60.8 KB
Line 
1/*
2 * pattern.c: Implementation of selectors for nodes
3 *
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
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
14/*
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
24 */
25
26#define IN_LIBXML
27#include "libxml.h"
28
29#include <string.h>
30#include <libxml/pattern.h>
31#include <libxml/xmlmemory.h>
32#include <libxml/tree.h>
33#include <libxml/dict.h>
34#include <libxml/xmlerror.h>
35#include <libxml/parserInternals.h>
36
37#ifdef LIBXML_PATTERN_ENABLED
38
39#ifdef ERROR
40#undef ERROR
41#endif
42#define ERROR(a, b, c, d)
43#define ERROR5(a, b, c, d, e)
44
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
51
52/*
53* NOTE: Those private flags (XML_STREAM_xxx) are used
54* in _xmlStreamCtxt->flag. They extend the public
55* xmlPatternFlags, so be careful not to interfere with the
56* reserved values for xmlPatternFlags.
57*/
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
61
62/*
63* XML_STREAM_ANY_NODE is used for comparison against
64* xmlElementType enums, to indicate a node of any type.
65*/
66#define XML_STREAM_ANY_NODE 100
67
68#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
69 XML_PATTERN_XSSEL | \
70 XML_PATTERN_XSFIELD)
71
72#define XML_STREAM_XS_IDC(c) ((c)->flags & \
73 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
74
75#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
76
77#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
78
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);
83
84#define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
85
86typedef struct _xmlStreamStep xmlStreamStep;
87typedef xmlStreamStep *xmlStreamStepPtr;
88struct _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 */
93};
94
95typedef struct _xmlStreamComp xmlStreamComp;
96typedef xmlStreamComp *xmlStreamCompPtr;
97struct _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;
103};
104
105struct _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;
114};
115
116static void xmlFreeStreamComp(xmlStreamCompPtr comp);
117
118/*
119 * Types are private:
120 */
121
122typedef 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;
133
134
135typedef struct _xmlStepState xmlStepState;
136typedef xmlStepState *xmlStepStatePtr;
137struct _xmlStepState {
138 int step;
139 xmlNodePtr node;
140};
141
142typedef struct _xmlStepStates xmlStepStates;
143typedef xmlStepStates *xmlStepStatesPtr;
144struct _xmlStepStates {
145 int nbstates;
146 int maxstates;
147 xmlStepStatePtr states;
148};
149
150typedef struct _xmlStepOp xmlStepOp;
151typedef xmlStepOp *xmlStepOpPtr;
152struct _xmlStepOp {
153 xmlPatOp op;
154 const xmlChar *value;
155 const xmlChar *value2; /* The namespace name */
156};
157
158#define PAT_FROM_ROOT (1<<8)
159#define PAT_FROM_CUR (1<<9)
160
161struct _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 */
171};
172
173typedef struct _xmlPatParserContext xmlPatParserContext;
174typedef xmlPatParserContext *xmlPatParserContextPtr;
175struct _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 */
184};
185
186/************************************************************************
187 * *
188 * Type functions *
189 * *
190 ************************************************************************/
191
192/**
193 * xmlNewPattern:
194 *
195 * Create a new XSLT Pattern
196 *
197 * Returns the newly allocated xmlPatternPtr or NULL in case of error
198 */
199static xmlPatternPtr
200xmlNewPattern(void) {
201 xmlPatternPtr cur;
202
203 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
204 if (cur == NULL) {
205 ERROR(NULL, NULL, NULL,
206 "xmlNewPattern : malloc failed\n");
207 return(NULL);
208 }
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);
217 }
218 return(cur);
219}
220
221/**
222 * xmlFreePattern:
223 * @comp: an XSLT comp
224 *
225 * Free up the memory allocated by @comp
226 */
227void
228xmlFreePattern(xmlPatternPtr comp) {
229 xmlFreePatternList(comp);
230}
231
232static void
233xmlFreePatternInternal(xmlPatternPtr comp) {
234 xmlStepOpPtr op;
235 int i;
236
237 if (comp == NULL)
238 return;
239 if (comp->stream != NULL)
240 xmlFreeStreamComp(comp->stream);
241 if (comp->pattern != NULL)
242 xmlFree((xmlChar *)comp->pattern);
243 if (comp->steps != NULL) {
244 if (comp->dict == NULL) {
245 for (i = 0;i < comp->nbStep;i++) {
246 op = &comp->steps[i];
247 if (op->value != NULL)
248 xmlFree((xmlChar *) op->value);
249 if (op->value2 != NULL)
250 xmlFree((xmlChar *) op->value2);
251 }
252 }
253 xmlFree(comp->steps);
254 }
255 if (comp->dict != NULL)
256 xmlDictFree(comp->dict);
257
258 memset(comp, -1, sizeof(xmlPattern));
259 xmlFree(comp);
260}
261
262/**
263 * xmlFreePatternList:
264 * @comp: an XSLT comp list
265 *
266 * Free up the memory allocated by all the elements of @comp
267 */
268void
269xmlFreePatternList(xmlPatternPtr comp) {
270 xmlPatternPtr cur;
271
272 while (comp != NULL) {
273 cur = comp;
274 comp = comp->next;
275 cur->next = NULL;
276 xmlFreePatternInternal(cur);
277 }
278}
279
280/**
281 * xmlNewPatParserContext:
282 * @pattern: the pattern context
283 * @dict: the inherited dictionary or NULL
284 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
285 * with [NULL, NULL] or NULL if no namespace is used
286 *
287 * Create a new XML pattern parser context
288 *
289 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
290 */
291static xmlPatParserContextPtr
292xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
293 const xmlChar **namespaces) {
294 xmlPatParserContextPtr cur;
295
296 if (pattern == NULL)
297 return(NULL);
298
299 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
300 if (cur == NULL) {
301 ERROR(NULL, NULL, NULL,
302 "xmlNewPatParserContext : malloc failed\n");
303 return(NULL);
304 }
305 memset(cur, 0, sizeof(xmlPatParserContext));
306 cur->dict = dict;
307 cur->cur = pattern;
308 cur->base = pattern;
309 if (namespaces != NULL) {
310 int i;
311 for (i = 0;namespaces[2 * i] != NULL;i++)
312 ;
313 cur->nb_namespaces = i;
314 } else {
315 cur->nb_namespaces = 0;
316 }
317 cur->namespaces = namespaces;
318 return(cur);
319}
320
321/**
322 * xmlFreePatParserContext:
323 * @ctxt: an XSLT parser context
324 *
325 * Free up the memory allocated by @ctxt
326 */
327static void
328xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
329 if (ctxt == NULL)
330 return;
331 memset(ctxt, -1, sizeof(xmlPatParserContext));
332 xmlFree(ctxt);
333}
334
335/**
336 * xmlPatternAdd:
337 * @comp: the compiled match expression
338 * @op: an op
339 * @value: the first value
340 * @value2: the second value
341 *
342 * Add a step to an XSLT Compiled Match
343 *
344 * Returns -1 in case of failure, 0 otherwise.
345 */
346static int
347xmlPatternAdd(xmlPatParserContextPtr ctxt, xmlPatternPtr comp,
348 xmlPatOp op, xmlChar * value, xmlChar * value2)
349{
350 if (comp->nbStep >= comp->maxStep) {
351 xmlStepOpPtr temp;
352 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
353 sizeof(xmlStepOp));
354 if (temp == NULL) {
355 ERROR(ctxt, NULL, NULL,
356 "xmlPatternAdd: realloc failed\n");
357 ctxt->error = -1;
358 return (-1);
359 }
360 comp->steps = temp;
361 comp->maxStep *= 2;
362 }
363 comp->steps[comp->nbStep].op = op;
364 comp->steps[comp->nbStep].value = value;
365 comp->steps[comp->nbStep].value2 = value2;
366 comp->nbStep++;
367 return (0);
368}
369
370#if 0
371/**
372 * xsltSwapTopPattern:
373 * @comp: the compiled match expression
374 *
375 * reverse the two top steps.
376 */
377static void
378xsltSwapTopPattern(xmlPatternPtr comp) {
379 int i;
380 int j = comp->nbStep - 1;
381
382 if (j > 0) {
383 register const xmlChar *tmp;
384 register xmlPatOp op;
385 i = j - 1;
386 tmp = comp->steps[i].value;
387 comp->steps[i].value = comp->steps[j].value;
388 comp->steps[j].value = tmp;
389 tmp = comp->steps[i].value2;
390 comp->steps[i].value2 = comp->steps[j].value2;
391 comp->steps[j].value2 = tmp;
392 op = comp->steps[i].op;
393 comp->steps[i].op = comp->steps[j].op;
394 comp->steps[j].op = op;
395 }
396}
397#endif
398
399/**
400 * xmlReversePattern:
401 * @comp: the compiled match expression
402 *
403 * reverse all the stack of expressions
404 *
405 * returns 0 in case of success and -1 in case of error.
406 */
407static int
408xmlReversePattern(xmlPatternPtr comp) {
409 int i, j;
410
411 /*
412 * remove the leading // for //a or .//a
413 */
414 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
415 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
416 comp->steps[i].value = comp->steps[j].value;
417 comp->steps[i].value2 = comp->steps[j].value2;
418 comp->steps[i].op = comp->steps[j].op;
419 }
420 comp->nbStep--;
421 }
422 if (comp->nbStep >= comp->maxStep) {
423 xmlStepOpPtr temp;
424 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
425 sizeof(xmlStepOp));
426 if (temp == NULL) {
427 ERROR(ctxt, NULL, NULL,
428 "xmlReversePattern: realloc failed\n");
429 return (-1);
430 }
431 comp->steps = temp;
432 comp->maxStep *= 2;
433 }
434 i = 0;
435 j = comp->nbStep - 1;
436 while (j > i) {
437 register const xmlChar *tmp;
438 register xmlPatOp op;
439 tmp = comp->steps[i].value;
440 comp->steps[i].value = comp->steps[j].value;
441 comp->steps[j].value = tmp;
442 tmp = comp->steps[i].value2;
443 comp->steps[i].value2 = comp->steps[j].value2;
444 comp->steps[j].value2 = tmp;
445 op = comp->steps[i].op;
446 comp->steps[i].op = comp->steps[j].op;
447 comp->steps[j].op = op;
448 j--;
449 i++;
450 }
451 comp->steps[comp->nbStep].value = NULL;
452 comp->steps[comp->nbStep].value2 = NULL;
453 comp->steps[comp->nbStep++].op = XML_OP_END;
454 return(0);
455}
456
457/************************************************************************
458 * *
459 * The interpreter for the precompiled patterns *
460 * *
461 ************************************************************************/
462
463static int
464xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
465 if ((states->states == NULL) || (states->maxstates <= 0)) {
466 states->maxstates = 4;
467 states->nbstates = 0;
468 states->states = xmlMalloc(4 * sizeof(xmlStepState));
469 }
470 else if (states->maxstates <= states->nbstates) {
471 xmlStepState *tmp;
472
473 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
474 2 * states->maxstates * sizeof(xmlStepState));
475 if (tmp == NULL)
476 return(-1);
477 states->states = tmp;
478 states->maxstates *= 2;
479 }
480 states->states[states->nbstates].step = step;
481 states->states[states->nbstates++].node = node;
482#if 0
483 fprintf(stderr, "Push: %d, %s\n", step, node->name);
484#endif
485 return(0);
486}
487
488/**
489 * xmlPatMatch:
490 * @comp: the precompiled pattern
491 * @node: a node
492 *
493 * Test whether the node matches the pattern
494 *
495 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
496 */
497static int
498xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
499 int i;
500 xmlStepOpPtr step;
501 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
502
503 if ((comp == NULL) || (node == NULL)) return(-1);
504 i = 0;
505restart:
506 for (;i < comp->nbStep;i++) {
507 step = &comp->steps[i];
508 switch (step->op) {
509 case XML_OP_END:
510 goto found;
511 case XML_OP_ROOT:
512 if (node->type == XML_NAMESPACE_DECL)
513 goto rollback;
514 node = node->parent;
515 if ((node->type == XML_DOCUMENT_NODE) ||
516 (node->type == XML_HTML_DOCUMENT_NODE))
517 continue;
518 goto rollback;
519 case XML_OP_ELEM:
520 if (node->type != XML_ELEMENT_NODE)
521 goto rollback;
522 if (step->value == NULL)
523 continue;
524 if (step->value[0] != node->name[0])
525 goto rollback;
526 if (!xmlStrEqual(step->value, node->name))
527 goto rollback;
528
529 /* Namespace test */
530 if (node->ns == NULL) {
531 if (step->value2 != NULL)
532 goto rollback;
533 } else if (node->ns->href != NULL) {
534 if (step->value2 == NULL)
535 goto rollback;
536 if (!xmlStrEqual(step->value2, node->ns->href))
537 goto rollback;
538 }
539 continue;
540 case XML_OP_CHILD: {
541 xmlNodePtr lst;
542
543 if ((node->type != XML_ELEMENT_NODE) &&
544 (node->type != XML_DOCUMENT_NODE) &&
545 (node->type != XML_HTML_DOCUMENT_NODE))
546 goto rollback;
547
548 lst = node->children;
549
550 if (step->value != NULL) {
551 while (lst != NULL) {
552 if ((lst->type == XML_ELEMENT_NODE) &&
553 (step->value[0] == lst->name[0]) &&
554 (xmlStrEqual(step->value, lst->name)))
555 break;
556 lst = lst->next;
557 }
558 if (lst != NULL)
559 continue;
560 }
561 goto rollback;
562 }
563 case XML_OP_ATTR:
564 if (node->type != XML_ATTRIBUTE_NODE)
565 goto rollback;
566 if (step->value != NULL) {
567 if (step->value[0] != node->name[0])
568 goto rollback;
569 if (!xmlStrEqual(step->value, node->name))
570 goto rollback;
571 }
572 /* Namespace test */
573 if (node->ns == NULL) {
574 if (step->value2 != NULL)
575 goto rollback;
576 } else if (step->value2 != NULL) {
577 if (!xmlStrEqual(step->value2, node->ns->href))
578 goto rollback;
579 }
580 continue;
581 case XML_OP_PARENT:
582 if ((node->type == XML_DOCUMENT_NODE) ||
583 (node->type == XML_HTML_DOCUMENT_NODE) ||
584 (node->type == XML_NAMESPACE_DECL))
585 goto rollback;
586 node = node->parent;
587 if (node == NULL)
588 goto rollback;
589 if (step->value == NULL)
590 continue;
591 if (step->value[0] != node->name[0])
592 goto rollback;
593 if (!xmlStrEqual(step->value, node->name))
594 goto rollback;
595 /* Namespace test */
596 if (node->ns == NULL) {
597 if (step->value2 != NULL)
598 goto rollback;
599 } else if (node->ns->href != NULL) {
600 if (step->value2 == NULL)
601 goto rollback;
602 if (!xmlStrEqual(step->value2, node->ns->href))
603 goto rollback;
604 }
605 continue;
606 case XML_OP_ANCESTOR:
607 /* TODO: implement coalescing of ANCESTOR/NODE ops */
608 if (step->value == NULL) {
609 i++;
610 step = &comp->steps[i];
611 if (step->op == XML_OP_ROOT)
612 goto found;
613 if (step->op != XML_OP_ELEM)
614 goto rollback;
615 if (step->value == NULL)
616 return(-1);
617 }
618 if (node == NULL)
619 goto rollback;
620 if ((node->type == XML_DOCUMENT_NODE) ||
621 (node->type == XML_HTML_DOCUMENT_NODE) ||
622 (node->type == XML_NAMESPACE_DECL))
623 goto rollback;
624 node = node->parent;
625 while (node != NULL) {
626 if ((node->type == XML_ELEMENT_NODE) &&
627 (step->value[0] == node->name[0]) &&
628 (xmlStrEqual(step->value, node->name))) {
629 /* Namespace test */
630 if (node->ns == NULL) {
631 if (step->value2 == NULL)
632 break;
633 } else if (node->ns->href != NULL) {
634 if ((step->value2 != NULL) &&
635 (xmlStrEqual(step->value2, node->ns->href)))
636 break;
637 }
638 }
639 node = node->parent;
640 }
641 if (node == NULL)
642 goto rollback;
643 /*
644 * prepare a potential rollback from here
645 * for ancestors of that node.
646 */
647 if (step->op == XML_OP_ANCESTOR)
648 xmlPatPushState(&states, i, node);
649 else
650 xmlPatPushState(&states, i - 1, node);
651 continue;
652 case XML_OP_NS:
653 if (node->type != XML_ELEMENT_NODE)
654 goto rollback;
655 if (node->ns == NULL) {
656 if (step->value != NULL)
657 goto rollback;
658 } else if (node->ns->href != NULL) {
659 if (step->value == NULL)
660 goto rollback;
661 if (!xmlStrEqual(step->value, node->ns->href))
662 goto rollback;
663 }
664 break;
665 case XML_OP_ALL:
666 if (node->type != XML_ELEMENT_NODE)
667 goto rollback;
668 break;
669 }
670 }
671found:
672 if (states.states != NULL) {
673 /* Free the rollback states */
674 xmlFree(states.states);
675 }
676 return(1);
677rollback:
678 /* got an error try to rollback */
679 if (states.states == NULL)
680 return(0);
681 if (states.nbstates <= 0) {
682 xmlFree(states.states);
683 return(0);
684 }
685 states.nbstates--;
686 i = states.states[states.nbstates].step;
687 node = states.states[states.nbstates].node;
688#if 0
689 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
690#endif
691 goto restart;
692}
693
694/************************************************************************
695 * *
696 * Dedicated parser for templates *
697 * *
698 ************************************************************************/
699
700#define CUR (*ctxt->cur)
701#define SKIP(val) ctxt->cur += (val)
702#define NXT(val) ctxt->cur[(val)]
703#define PEEKPREV(val) ctxt->cur[-(val)]
704#define CUR_PTR ctxt->cur
705
706#define SKIP_BLANKS \
707 while (IS_BLANK_CH(CUR)) NEXT
708
709#define CURRENT (*ctxt->cur)
710#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
711
712
713#define PUSH(op, val, val2) \
714 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
715
716#if 0
717/**
718 * xmlPatScanLiteral:
719 * @ctxt: the XPath Parser context
720 *
721 * Parse an XPath Literal:
722 *
723 * [29] Literal ::= '"' [^"]* '"'
724 * | "'" [^']* "'"
725 *
726 * Returns the Literal parsed or NULL
727 */
728
729static xmlChar *
730xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
731 const xmlChar *q, *cur;
732 xmlChar *ret = NULL;
733 int val, len;
734
735 SKIP_BLANKS;
736 if (CUR == '"') {
737 NEXT;
738 cur = q = CUR_PTR;
739 val = xmlStringCurrentChar(NULL, cur, &len);
740 while ((IS_CHAR(val)) && (val != '"')) {
741 cur += len;
742 val = xmlStringCurrentChar(NULL, cur, &len);
743 }
744 if (!IS_CHAR(val)) {
745 ctxt->error = 1;
746 return(NULL);
747 } else {
748 if (ctxt->dict)
749 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
750 else
751 ret = xmlStrndup(q, cur - q);
752 }
753 cur += len;
754 CUR_PTR = cur;
755 } else if (CUR == '\'') {
756 NEXT;
757 cur = q = CUR_PTR;
758 val = xmlStringCurrentChar(NULL, cur, &len);
759 while ((IS_CHAR(val)) && (val != '\'')) {
760 cur += len;
761 val = xmlStringCurrentChar(NULL, cur, &len);
762 }
763 if (!IS_CHAR(val)) {
764 ctxt->error = 1;
765 return(NULL);
766 } else {
767 if (ctxt->dict)
768 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
769 else
770 ret = xmlStrndup(q, cur - q);
771 }
772 cur += len;
773 CUR_PTR = cur;
774 } else {
775 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
776 ctxt->error = 1;
777 return(NULL);
778 }
779 return(ret);
780}
781#endif
782
783/**
784 * xmlPatScanName:
785 * @ctxt: the XPath Parser context
786 *
787 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
788 * CombiningChar | Extender
789 *
790 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
791 *
792 * [6] Names ::= Name (S Name)*
793 *
794 * Returns the Name parsed or NULL
795 */
796
797static xmlChar *
798xmlPatScanName(xmlPatParserContextPtr ctxt) {
799 const xmlChar *q, *cur;
800 xmlChar *ret = NULL;
801 int val, len;
802
803 SKIP_BLANKS;
804
805 cur = q = CUR_PTR;
806 val = xmlStringCurrentChar(NULL, cur, &len);
807 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
808 return(NULL);
809
810 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
811 (val == '.') || (val == '-') ||
812 (val == '_') ||
813 (IS_COMBINING(val)) ||
814 (IS_EXTENDER(val))) {
815 cur += len;
816 val = xmlStringCurrentChar(NULL, cur, &len);
817 }
818 if (ctxt->dict)
819 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
820 else
821 ret = xmlStrndup(q, cur - q);
822 CUR_PTR = cur;
823 return(ret);
824}
825
826/**
827 * xmlPatScanNCName:
828 * @ctxt: the XPath Parser context
829 *
830 * Parses a non qualified name
831 *
832 * Returns the Name parsed or NULL
833 */
834
835static xmlChar *
836xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
837 const xmlChar *q, *cur;
838 xmlChar *ret = NULL;
839 int val, len;
840
841 SKIP_BLANKS;
842
843 cur = q = CUR_PTR;
844 val = xmlStringCurrentChar(NULL, cur, &len);
845 if (!IS_LETTER(val) && (val != '_'))
846 return(NULL);
847
848 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
849 (val == '.') || (val == '-') ||
850 (val == '_') ||
851 (IS_COMBINING(val)) ||
852 (IS_EXTENDER(val))) {
853 cur += len;
854 val = xmlStringCurrentChar(NULL, cur, &len);
855 }
856 if (ctxt->dict)
857 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
858 else
859 ret = xmlStrndup(q, cur - q);
860 if (ret == NULL)
861 ctxt->error = -1;
862 CUR_PTR = cur;
863 return(ret);
864}
865
866#if 0
867/**
868 * xmlPatScanQName:
869 * @ctxt: the XPath Parser context
870 * @prefix: the place to store the prefix
871 *
872 * Parse a qualified name
873 *
874 * Returns the Name parsed or NULL
875 */
876
877static xmlChar *
878xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
879 xmlChar *ret = NULL;
880
881 *prefix = NULL;
882 ret = xmlPatScanNCName(ctxt);
883 if (CUR == ':') {
884 *prefix = ret;
885 NEXT;
886 ret = xmlPatScanNCName(ctxt);
887 }
888 return(ret);
889}
890#endif
891
892/**
893 * xmlCompileAttributeTest:
894 * @ctxt: the compilation context
895 *
896 * Compile an attribute test.
897 */
898static void
899xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
900 xmlChar *token = NULL;
901 xmlChar *name = NULL;
902 xmlChar *URL = NULL;
903
904 SKIP_BLANKS;
905 name = xmlPatScanNCName(ctxt);
906 if (ctxt->error < 0)
907 return;
908 if (name == NULL) {
909 if (CUR == '*') {
910 PUSH(XML_OP_ATTR, NULL, NULL);
911 NEXT;
912 } else {
913 ERROR(NULL, NULL, NULL,
914 "xmlCompileAttributeTest : Name expected\n");
915 ctxt->error = 1;
916 }
917 return;
918 }
919 if (CUR == ':') {
920 int i;
921 xmlChar *prefix = name;
922
923 NEXT;
924
925 if (IS_BLANK_CH(CUR)) {
926 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
927 ctxt->error = 1;
928 goto error;
929 }
930 /*
931 * This is a namespace match
932 */
933 token = xmlPatScanName(ctxt);
934 if ((prefix[0] == 'x') &&
935 (prefix[1] == 'm') &&
936 (prefix[2] == 'l') &&
937 (prefix[3] == 0))
938 {
939 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
940 } else {
941 for (i = 0;i < ctxt->nb_namespaces;i++) {
942 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
943 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
944 break;
945 }
946 }
947 if (i >= ctxt->nb_namespaces) {
948 ERROR5(NULL, NULL, NULL,
949 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
950 prefix);
951 ctxt->error = 1;
952 goto error;
953 }
954 }
955 XML_PAT_FREE_STRING(ctxt, name);
956 name = NULL;
957 if (token == NULL) {
958 if (CUR == '*') {
959 NEXT;
960 PUSH(XML_OP_ATTR, NULL, URL);
961 } else {
962 ERROR(NULL, NULL, NULL,
963 "xmlCompileAttributeTest : Name expected\n");
964 ctxt->error = 1;
965 goto error;
966 }
967 } else {
968 PUSH(XML_OP_ATTR, token, URL);
969 }
970 } else {
971 PUSH(XML_OP_ATTR, name, NULL);
972 }
973 return;
974error:
975 if (name != NULL)
976 XML_PAT_FREE_STRING(ctxt, name);
977 if (URL != NULL)
978 XML_PAT_FREE_STRING(ctxt, URL)
979 if (token != NULL)
980 XML_PAT_FREE_STRING(ctxt, token);
981}
982
983/**
984 * xmlCompileStepPattern:
985 * @ctxt: the compilation context
986 *
987 * Compile the Step Pattern and generates a precompiled
988 * form suitable for fast matching.
989 *
990 * [3] Step ::= '.' | NameTest
991 * [4] NameTest ::= QName | '*' | NCName ':' '*'
992 */
993
994static void
995xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
996 xmlChar *token = NULL;
997 xmlChar *name = NULL;
998 xmlChar *URL = NULL;
999 int hasBlanks = 0;
1000
1001 SKIP_BLANKS;
1002 if (CUR == '.') {
1003 /*
1004 * Context node.
1005 */
1006 NEXT;
1007 PUSH(XML_OP_ELEM, NULL, NULL);
1008 return;
1009 }
1010 if (CUR == '@') {
1011 /*
1012 * Attribute test.
1013 */
1014 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1015 ERROR5(NULL, NULL, NULL,
1016 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1017 ctxt->error = 1;
1018 return;
1019 }
1020 NEXT;
1021 xmlCompileAttributeTest(ctxt);
1022 if (ctxt->error != 0)
1023 goto error;
1024 return;
1025 }
1026 name = xmlPatScanNCName(ctxt);
1027 if (ctxt->error < 0)
1028 return;
1029 if (name == NULL) {
1030 if (CUR == '*') {
1031 NEXT;
1032 PUSH(XML_OP_ALL, NULL, NULL);
1033 return;
1034 } else {
1035 ERROR(NULL, NULL, NULL,
1036 "xmlCompileStepPattern : Name expected\n");
1037 ctxt->error = 1;
1038 return;
1039 }
1040 }
1041 if (IS_BLANK_CH(CUR)) {
1042 hasBlanks = 1;
1043 SKIP_BLANKS;
1044 }
1045 if (CUR == ':') {
1046 NEXT;
1047 if (CUR != ':') {
1048 xmlChar *prefix = name;
1049 int i;
1050
1051 if (hasBlanks || IS_BLANK_CH(CUR)) {
1052 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1053 ctxt->error = 1;
1054 goto error;
1055 }
1056 /*
1057 * This is a namespace match
1058 */
1059 token = xmlPatScanName(ctxt);
1060 if ((prefix[0] == 'x') &&
1061 (prefix[1] == 'm') &&
1062 (prefix[2] == 'l') &&
1063 (prefix[3] == 0))
1064 {
1065 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1066 } else {
1067 for (i = 0;i < ctxt->nb_namespaces;i++) {
1068 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1069 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1070 break;
1071 }
1072 }
1073 if (i >= ctxt->nb_namespaces) {
1074 ERROR5(NULL, NULL, NULL,
1075 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1076 prefix);
1077 ctxt->error = 1;
1078 goto error;
1079 }
1080 }
1081 XML_PAT_FREE_STRING(ctxt, prefix);
1082 name = NULL;
1083 if (token == NULL) {
1084 if (CUR == '*') {
1085 NEXT;
1086 PUSH(XML_OP_NS, URL, NULL);
1087 } else {
1088 ERROR(NULL, NULL, NULL,
1089 "xmlCompileStepPattern : Name expected\n");
1090 ctxt->error = 1;
1091 goto error;
1092 }
1093 } else {
1094 PUSH(XML_OP_ELEM, token, URL);
1095 }
1096 } else {
1097 NEXT;
1098 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1099 XML_PAT_FREE_STRING(ctxt, name);
1100 name = xmlPatScanName(ctxt);
1101 if (name == NULL) {
1102 if (CUR == '*') {
1103 NEXT;
1104 PUSH(XML_OP_ALL, NULL, NULL);
1105 return;
1106 } else {
1107 ERROR(NULL, NULL, NULL,
1108 "xmlCompileStepPattern : QName expected\n");
1109 ctxt->error = 1;
1110 goto error;
1111 }
1112 }
1113 if (CUR == ':') {
1114 xmlChar *prefix = name;
1115 int i;
1116
1117 NEXT;
1118 if (IS_BLANK_CH(CUR)) {
1119 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1120 ctxt->error = 1;
1121 goto error;
1122 }
1123 /*
1124 * This is a namespace match
1125 */
1126 token = xmlPatScanName(ctxt);
1127 if ((prefix[0] == 'x') &&
1128 (prefix[1] == 'm') &&
1129 (prefix[2] == 'l') &&
1130 (prefix[3] == 0))
1131 {
1132 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1133 } else {
1134 for (i = 0;i < ctxt->nb_namespaces;i++) {
1135 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1136 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1137 break;
1138 }
1139 }
1140 if (i >= ctxt->nb_namespaces) {
1141 ERROR5(NULL, NULL, NULL,
1142 "xmlCompileStepPattern : no namespace bound "
1143 "to prefix %s\n", prefix);
1144 ctxt->error = 1;
1145 goto error;
1146 }
1147 }
1148 XML_PAT_FREE_STRING(ctxt, prefix);
1149 name = NULL;
1150 if (token == NULL) {
1151 if (CUR == '*') {
1152 NEXT;
1153 PUSH(XML_OP_NS, URL, NULL);
1154 } else {
1155 ERROR(NULL, NULL, NULL,
1156 "xmlCompileStepPattern : Name expected\n");
1157 ctxt->error = 1;
1158 goto error;
1159 }
1160 } else {
1161 PUSH(XML_OP_CHILD, token, URL);
1162 }
1163 } else
1164 PUSH(XML_OP_CHILD, name, NULL);
1165 return;
1166 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1167 XML_PAT_FREE_STRING(ctxt, name)
1168 name = NULL;
1169 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1170 ERROR5(NULL, NULL, NULL,
1171 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1172 ctxt->error = 1;
1173 goto error;
1174 }
1175 xmlCompileAttributeTest(ctxt);
1176 if (ctxt->error != 0)
1177 goto error;
1178 return;
1179 } else {
1180 ERROR5(NULL, NULL, NULL,
1181 "The 'element' or 'attribute' axis is expected.\n", NULL);
1182 ctxt->error = 1;
1183 goto error;
1184 }
1185 }
1186 } else if (CUR == '*') {
1187 if (name != NULL) {
1188 ctxt->error = 1;
1189 goto error;
1190 }
1191 NEXT;
1192 PUSH(XML_OP_ALL, token, NULL);
1193 } else {
1194 PUSH(XML_OP_ELEM, name, NULL);
1195 }
1196 return;
1197error:
1198 if (URL != NULL)
1199 XML_PAT_FREE_STRING(ctxt, URL)
1200 if (token != NULL)
1201 XML_PAT_FREE_STRING(ctxt, token)
1202 if (name != NULL)
1203 XML_PAT_FREE_STRING(ctxt, name)
1204}
1205
1206/**
1207 * xmlCompilePathPattern:
1208 * @ctxt: the compilation context
1209 *
1210 * Compile the Path Pattern and generates a precompiled
1211 * form suitable for fast matching.
1212 *
1213 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1214 */
1215static void
1216xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1217 SKIP_BLANKS;
1218 if (CUR == '/') {
1219 ctxt->comp->flags |= PAT_FROM_ROOT;
1220 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1221 ctxt->comp->flags |= PAT_FROM_CUR;
1222 }
1223
1224 if ((CUR == '/') && (NXT(1) == '/')) {
1225 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1226 NEXT;
1227 NEXT;
1228 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1229 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1230 NEXT;
1231 NEXT;
1232 NEXT;
1233 /* Check for incompleteness. */
1234 SKIP_BLANKS;
1235 if (CUR == 0) {
1236 ERROR5(NULL, NULL, NULL,
1237 "Incomplete expression '%s'.\n", ctxt->base);
1238 ctxt->error = 1;
1239 goto error;
1240 }
1241 }
1242 if (CUR == '@') {
1243 NEXT;
1244 xmlCompileAttributeTest(ctxt);
1245 if (ctxt->error != 0)
1246 goto error;
1247 SKIP_BLANKS;
1248 /* TODO: check for incompleteness */
1249 if (CUR != 0) {
1250 xmlCompileStepPattern(ctxt);
1251 if (ctxt->error != 0)
1252 goto error;
1253 }
1254 } else {
1255 if (CUR == '/') {
1256 PUSH(XML_OP_ROOT, NULL, NULL);
1257 NEXT;
1258 /* Check for incompleteness. */
1259 SKIP_BLANKS;
1260 if (CUR == 0) {
1261 ERROR5(NULL, NULL, NULL,
1262 "Incomplete expression '%s'.\n", ctxt->base);
1263 ctxt->error = 1;
1264 goto error;
1265 }
1266 }
1267 xmlCompileStepPattern(ctxt);
1268 if (ctxt->error != 0)
1269 goto error;
1270 SKIP_BLANKS;
1271 while (CUR == '/') {
1272 if (NXT(1) == '/') {
1273 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1274 NEXT;
1275 NEXT;
1276 SKIP_BLANKS;
1277 xmlCompileStepPattern(ctxt);
1278 if (ctxt->error != 0)
1279 goto error;
1280 } else {
1281 PUSH(XML_OP_PARENT, NULL, NULL);
1282 NEXT;
1283 SKIP_BLANKS;
1284 if (CUR == 0) {
1285 ERROR5(NULL, NULL, NULL,
1286 "Incomplete expression '%s'.\n", ctxt->base);
1287 ctxt->error = 1;
1288 goto error;
1289 }
1290 xmlCompileStepPattern(ctxt);
1291 if (ctxt->error != 0)
1292 goto error;
1293 }
1294 }
1295 }
1296 if (CUR != 0) {
1297 ERROR5(NULL, NULL, NULL,
1298 "Failed to compile pattern %s\n", ctxt->base);
1299 ctxt->error = 1;
1300 }
1301error:
1302 return;
1303}
1304
1305/**
1306 * xmlCompileIDCXPathPath:
1307 * @ctxt: the compilation context
1308 *
1309 * Compile the Path Pattern and generates a precompiled
1310 * form suitable for fast matching.
1311 *
1312 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1313 */
1314static void
1315xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1316 SKIP_BLANKS;
1317 if (CUR == '/') {
1318 ERROR5(NULL, NULL, NULL,
1319 "Unexpected selection of the document root in '%s'.\n",
1320 ctxt->base);
1321 goto error;
1322 }
1323 ctxt->comp->flags |= PAT_FROM_CUR;
1324
1325 if (CUR == '.') {
1326 /* "." - "self::node()" */
1327 NEXT;
1328 SKIP_BLANKS;
1329 if (CUR == 0) {
1330 /*
1331 * Selection of the context node.
1332 */
1333 PUSH(XML_OP_ELEM, NULL, NULL);
1334 return;
1335 }
1336 if (CUR != '/') {
1337 /* TODO: A more meaningful error message. */
1338 ERROR5(NULL, NULL, NULL,
1339 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1340 goto error;
1341 }
1342 /* "./" - "self::node()/" */
1343 NEXT;
1344 SKIP_BLANKS;
1345 if (CUR == '/') {
1346 if (IS_BLANK_CH(PEEKPREV(1))) {
1347 /*
1348 * Disallow "./ /"
1349 */
1350 ERROR5(NULL, NULL, NULL,
1351 "Unexpected '/' token in '%s'.\n", ctxt->base);
1352 goto error;
1353 }
1354 /* ".//" - "self:node()/descendant-or-self::node()/" */
1355 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1356 NEXT;
1357 SKIP_BLANKS;
1358 }
1359 if (CUR == 0)
1360 goto error_unfinished;
1361 }
1362 /*
1363 * Process steps.
1364 */
1365 do {
1366 xmlCompileStepPattern(ctxt);
1367 if (ctxt->error != 0)
1368 goto error;
1369 SKIP_BLANKS;
1370 if (CUR != '/')
1371 break;
1372 PUSH(XML_OP_PARENT, NULL, NULL);
1373 NEXT;
1374 SKIP_BLANKS;
1375 if (CUR == '/') {
1376 /*
1377 * Disallow subsequent '//'.
1378 */
1379 ERROR5(NULL, NULL, NULL,
1380 "Unexpected subsequent '//' in '%s'.\n",
1381 ctxt->base);
1382 goto error;
1383 }
1384 if (CUR == 0)
1385 goto error_unfinished;
1386
1387 } while (CUR != 0);
1388
1389 if (CUR != 0) {
1390 ERROR5(NULL, NULL, NULL,
1391 "Failed to compile expression '%s'.\n", ctxt->base);
1392 ctxt->error = 1;
1393 }
1394 return;
1395error:
1396 ctxt->error = 1;
1397 return;
1398
1399error_unfinished:
1400 ctxt->error = 1;
1401 ERROR5(NULL, NULL, NULL,
1402 "Unfinished expression '%s'.\n", ctxt->base);
1403 return;
1404}
1405
1406/************************************************************************
1407 * *
1408 * The streaming code *
1409 * *
1410 ************************************************************************/
1411
1412/**
1413 * xmlNewStreamComp:
1414 * @size: the number of expected steps
1415 *
1416 * build a new compiled pattern for streaming
1417 *
1418 * Returns the new structure or NULL in case of error.
1419 */
1420static xmlStreamCompPtr
1421xmlNewStreamComp(int size) {
1422 xmlStreamCompPtr cur;
1423
1424 if (size < 4)
1425 size = 4;
1426
1427 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1428 if (cur == NULL) {
1429 ERROR(NULL, NULL, NULL,
1430 "xmlNewStreamComp: malloc failed\n");
1431 return(NULL);
1432 }
1433 memset(cur, 0, sizeof(xmlStreamComp));
1434 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1435 if (cur->steps == NULL) {
1436 xmlFree(cur);
1437 ERROR(NULL, NULL, NULL,
1438 "xmlNewStreamComp: malloc failed\n");
1439 return(NULL);
1440 }
1441 cur->nbStep = 0;
1442 cur->maxStep = size;
1443 return(cur);
1444}
1445
1446/**
1447 * xmlFreeStreamComp:
1448 * @comp: the compiled pattern for streaming
1449 *
1450 * Free the compiled pattern for streaming
1451 */
1452static void
1453xmlFreeStreamComp(xmlStreamCompPtr comp) {
1454 if (comp != NULL) {
1455 if (comp->steps != NULL)
1456 xmlFree(comp->steps);
1457 if (comp->dict != NULL)
1458 xmlDictFree(comp->dict);
1459 xmlFree(comp);
1460 }
1461}
1462
1463/**
1464 * xmlStreamCompAddStep:
1465 * @comp: the compiled pattern for streaming
1466 * @name: the first string, the name, or NULL for *
1467 * @ns: the second step, the namespace name
1468 * @flags: the flags for that step
1469 *
1470 * Add a new step to the compiled pattern
1471 *
1472 * Returns -1 in case of error or the step index if successful
1473 */
1474static int
1475xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1476 const xmlChar *ns, int nodeType, int flags) {
1477 xmlStreamStepPtr cur;
1478
1479 if (comp->nbStep >= comp->maxStep) {
1480 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1481 comp->maxStep * 2 * sizeof(xmlStreamStep));
1482 if (cur == NULL) {
1483 ERROR(NULL, NULL, NULL,
1484 "xmlNewStreamComp: malloc failed\n");
1485 return(-1);
1486 }
1487 comp->steps = cur;
1488 comp->maxStep *= 2;
1489 }
1490 cur = &comp->steps[comp->nbStep++];
1491 cur->flags = flags;
1492 cur->name = name;
1493 cur->ns = ns;
1494 cur->nodeType = nodeType;
1495 return(comp->nbStep - 1);
1496}
1497
1498/**
1499 * xmlStreamCompile:
1500 * @comp: the precompiled pattern
1501 *
1502 * Tries to stream compile a pattern
1503 *
1504 * Returns -1 in case of failure and 0 in case of success.
1505 */
1506static int
1507xmlStreamCompile(xmlPatternPtr comp) {
1508 xmlStreamCompPtr stream;
1509 int i, s = 0, root = 0, flags = 0, prevs = -1;
1510 xmlStepOp step;
1511
1512 if ((comp == NULL) || (comp->steps == NULL))
1513 return(-1);
1514 /*
1515 * special case for .
1516 */
1517 if ((comp->nbStep == 1) &&
1518 (comp->steps[0].op == XML_OP_ELEM) &&
1519 (comp->steps[0].value == NULL) &&
1520 (comp->steps[0].value2 == NULL)) {
1521 stream = xmlNewStreamComp(0);
1522 if (stream == NULL)
1523 return(-1);
1524 /* Note that the stream will have no steps in this case. */
1525 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1526 comp->stream = stream;
1527 return(0);
1528 }
1529
1530 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1531 if (stream == NULL)
1532 return(-1);
1533 if (comp->dict != NULL) {
1534 stream->dict = comp->dict;
1535 xmlDictReference(stream->dict);
1536 }
1537
1538 i = 0;
1539 if (comp->flags & PAT_FROM_ROOT)
1540 stream->flags |= XML_STREAM_FROM_ROOT;
1541
1542 for (;i < comp->nbStep;i++) {
1543 step = comp->steps[i];
1544 switch (step.op) {
1545 case XML_OP_END:
1546 break;
1547 case XML_OP_ROOT:
1548 if (i != 0)
1549 goto error;
1550 root = 1;
1551 break;
1552 case XML_OP_NS:
1553 s = xmlStreamCompAddStep(stream, NULL, step.value,
1554 XML_ELEMENT_NODE, flags);
1555 if (s < 0)
1556 goto error;
1557 prevs = s;
1558 flags = 0;
1559 break;
1560 case XML_OP_ATTR:
1561 flags |= XML_STREAM_STEP_ATTR;
1562 prevs = -1;
1563 s = xmlStreamCompAddStep(stream,
1564 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1565 flags = 0;
1566 if (s < 0)
1567 goto error;
1568 break;
1569 case XML_OP_ELEM:
1570 if ((step.value == NULL) && (step.value2 == NULL)) {
1571 /*
1572 * We have a "." or "self::node()" here.
1573 * Eliminate redundant self::node() tests like in "/./."
1574 * or "//./"
1575 * The only case we won't eliminate is "//.", i.e. if
1576 * self::node() is the last node test and we had
1577 * continuation somewhere beforehand.
1578 */
1579 if ((comp->nbStep == i + 1) &&
1580 (flags & XML_STREAM_STEP_DESC)) {
1581 /*
1582 * Mark the special case where the expression resolves
1583 * to any type of node.
1584 */
1585 if (comp->nbStep == i + 1) {
1586 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1587 }
1588 flags |= XML_STREAM_STEP_NODE;
1589 s = xmlStreamCompAddStep(stream, NULL, NULL,
1590 XML_STREAM_ANY_NODE, flags);
1591 if (s < 0)
1592 goto error;
1593 flags = 0;
1594 /*
1595 * If there was a previous step, mark it to be added to
1596 * the result node-set; this is needed since only
1597 * the last step will be marked as "final" and only
1598 * "final" nodes are added to the resulting set.
1599 */
1600 if (prevs != -1) {
1601 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1602 prevs = -1;
1603 }
1604 break;
1605
1606 } else {
1607 /* Just skip this one. */
1608 continue;
1609 }
1610 }
1611 /* An element node. */
1612 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1613 XML_ELEMENT_NODE, flags);
1614 if (s < 0)
1615 goto error;
1616 prevs = s;
1617 flags = 0;
1618 break;
1619 case XML_OP_CHILD:
1620 /* An element node child. */
1621 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1622 XML_ELEMENT_NODE, flags);
1623 if (s < 0)
1624 goto error;
1625 prevs = s;
1626 flags = 0;
1627 break;
1628 case XML_OP_ALL:
1629 s = xmlStreamCompAddStep(stream, NULL, NULL,
1630 XML_ELEMENT_NODE, flags);
1631 if (s < 0)
1632 goto error;
1633 prevs = s;
1634 flags = 0;
1635 break;
1636 case XML_OP_PARENT:
1637 break;
1638 case XML_OP_ANCESTOR:
1639 /* Skip redundant continuations. */
1640 if (flags & XML_STREAM_STEP_DESC)
1641 break;
1642 flags |= XML_STREAM_STEP_DESC;
1643 /*
1644 * Mark the expression as having "//".
1645 */
1646 if ((stream->flags & XML_STREAM_DESC) == 0)
1647 stream->flags |= XML_STREAM_DESC;
1648 break;
1649 }
1650 }
1651 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1652 /*
1653 * If this should behave like a real pattern, we will mark
1654 * the first step as having "//", to be reentrant on every
1655 * tree level.
1656 */
1657 if ((stream->flags & XML_STREAM_DESC) == 0)
1658 stream->flags |= XML_STREAM_DESC;
1659
1660 if (stream->nbStep > 0) {
1661 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1662 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1663 }
1664 }
1665 if (stream->nbStep <= s)
1666 goto error;
1667 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1668 if (root)
1669 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1670 comp->stream = stream;
1671 return(0);
1672error:
1673 xmlFreeStreamComp(stream);
1674 return(0);
1675}
1676
1677/**
1678 * xmlNewStreamCtxt:
1679 * @size: the number of expected states
1680 *
1681 * build a new stream context
1682 *
1683 * Returns the new structure or NULL in case of error.
1684 */
1685static xmlStreamCtxtPtr
1686xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1687 xmlStreamCtxtPtr cur;
1688
1689 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1690 if (cur == NULL) {
1691 ERROR(NULL, NULL, NULL,
1692 "xmlNewStreamCtxt: malloc failed\n");
1693 return(NULL);
1694 }
1695 memset(cur, 0, sizeof(xmlStreamCtxt));
1696 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1697 if (cur->states == NULL) {
1698 xmlFree(cur);
1699 ERROR(NULL, NULL, NULL,
1700 "xmlNewStreamCtxt: malloc failed\n");
1701 return(NULL);
1702 }
1703 cur->nbState = 0;
1704 cur->maxState = 4;
1705 cur->level = 0;
1706 cur->comp = stream;
1707 cur->blockLevel = -1;
1708 return(cur);
1709}
1710
1711/**
1712 * xmlFreeStreamCtxt:
1713 * @stream: the stream context
1714 *
1715 * Free the stream context
1716 */
1717void
1718xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1719 xmlStreamCtxtPtr next;
1720
1721 while (stream != NULL) {
1722 next = stream->next;
1723 if (stream->states != NULL)
1724 xmlFree(stream->states);
1725 xmlFree(stream);
1726 stream = next;
1727 }
1728}
1729
1730/**
1731 * xmlStreamCtxtAddState:
1732 * @comp: the stream context
1733 * @idx: the step index for that streaming state
1734 *
1735 * Add a new state to the stream context
1736 *
1737 * Returns -1 in case of error or the state index if successful
1738 */
1739static int
1740xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1741 int i;
1742 for (i = 0;i < comp->nbState;i++) {
1743 if (comp->states[2 * i] < 0) {
1744 comp->states[2 * i] = idx;
1745 comp->states[2 * i + 1] = level;
1746 return(i);
1747 }
1748 }
1749 if (comp->nbState >= comp->maxState) {
1750 int *cur;
1751
1752 cur = (int *) xmlRealloc(comp->states,
1753 comp->maxState * 4 * sizeof(int));
1754 if (cur == NULL) {
1755 ERROR(NULL, NULL, NULL,
1756 "xmlNewStreamCtxt: malloc failed\n");
1757 return(-1);
1758 }
1759 comp->states = cur;
1760 comp->maxState *= 2;
1761 }
1762 comp->states[2 * comp->nbState] = idx;
1763 comp->states[2 * comp->nbState++ + 1] = level;
1764 return(comp->nbState - 1);
1765}
1766
1767/**
1768 * xmlStreamPushInternal:
1769 * @stream: the stream context
1770 * @name: the current name
1771 * @ns: the namespace name
1772 * @nodeType: the type of the node
1773 *
1774 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1775 * indicated a dictionary, then strings for name and ns will be expected
1776 * to come from the dictionary.
1777 * Both @name and @ns being NULL means the / i.e. the root of the document.
1778 * This can also act as a reset.
1779 *
1780 * Returns: -1 in case of error, 1 if the current state in the stream is a
1781 * match and 0 otherwise.
1782 */
1783static int
1784xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1785 const xmlChar *name, const xmlChar *ns,
1786 int nodeType) {
1787 int ret = 0, final = 0, tmp, i, m, match, stepNr, desc;
1788 xmlStreamCompPtr comp;
1789 xmlStreamStep step;
1790
1791 if ((stream == NULL) || (stream->nbState < 0))
1792 return(-1);
1793
1794 while (stream != NULL) {
1795 comp = stream->comp;
1796
1797 if ((nodeType == XML_ELEMENT_NODE) &&
1798 (name == NULL) && (ns == NULL)) {
1799 /* We have a document node here (or a reset). */
1800 stream->nbState = 0;
1801 stream->level = 0;
1802 stream->blockLevel = -1;
1803 if (comp->flags & XML_STREAM_FROM_ROOT) {
1804 if (comp->nbStep == 0) {
1805 /* TODO: We have a "/." here? */
1806 ret = 1;
1807 } else {
1808 if ((comp->nbStep == 1) &&
1809 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1810 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1811 {
1812 /*
1813 * In the case of "//." the document node will match
1814 * as well.
1815 */
1816 ret = 1;
1817 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1818 if (xmlStreamCtxtAddState(stream, 0, 0) < 0)
1819 return(-1);
1820 }
1821 }
1822 }
1823 stream = stream->next;
1824 continue; /* while */
1825 }
1826
1827 /*
1828 * Fast check for ".".
1829 */
1830 if (comp->nbStep == 0) {
1831 /*
1832 * / and . are handled at the XPath node set creation
1833 * level by checking min depth
1834 */
1835 if (stream->flags & XML_PATTERN_XPATH) {
1836 stream = stream->next;
1837 continue; /* while */
1838 }
1839 /*
1840 * For non-pattern like evaluation like XML Schema IDCs
1841 * or traditional XPath expressions, this will match if
1842 * we are at the first level only, otherwise on every level.
1843 */
1844 if ((nodeType != XML_ATTRIBUTE_NODE) &&
1845 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1846 (stream->level == 0))) {
1847 ret = 1;
1848 }
1849 stream->level++;
1850 goto stream_next;
1851 }
1852 if (stream->blockLevel != -1) {
1853 /*
1854 * Skip blocked expressions.
1855 */
1856 stream->level++;
1857 goto stream_next;
1858 }
1859
1860 if ((nodeType != XML_ELEMENT_NODE) &&
1861 (nodeType != XML_ATTRIBUTE_NODE) &&
1862 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1863 /*
1864 * No need to process nodes of other types if we don't
1865 * resolve to those types.
1866 * TODO: Do we need to block the context here?
1867 */
1868 stream->level++;
1869 goto stream_next;
1870 }
1871
1872 /*
1873 * Check evolution of existing states
1874 */
1875 i = 0;
1876 m = stream->nbState;
1877 while (i < m) {
1878 if ((comp->flags & XML_STREAM_DESC) == 0) {
1879 /*
1880 * If there is no "//", then only the last
1881 * added state is of interest.
1882 */
1883 stepNr = stream->states[2 * (stream->nbState -1)];
1884 /*
1885 * TODO: Security check, should not happen, remove it.
1886 */
1887 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1888 stream->level) {
1889 return (-1);
1890 }
1891 desc = 0;
1892 /* loop-stopper */
1893 i = m;
1894 } else {
1895 /*
1896 * If there are "//", then we need to process every "//"
1897 * occurring in the states, plus any other state for this
1898 * level.
1899 */
1900 stepNr = stream->states[2 * i];
1901
1902 /* TODO: should not happen anymore: dead states */
1903 if (stepNr < 0)
1904 goto next_state;
1905
1906 tmp = stream->states[(2 * i) + 1];
1907
1908 /* skip new states just added */
1909 if (tmp > stream->level)
1910 goto next_state;
1911
1912 /* skip states at ancestor levels, except if "//" */
1913 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1914 if ((tmp < stream->level) && (!desc))
1915 goto next_state;
1916 }
1917 /*
1918 * Check for correct node-type.
1919 */
1920 step = comp->steps[stepNr];
1921 if (step.nodeType != nodeType) {
1922 if (step.nodeType == XML_ATTRIBUTE_NODE) {
1923 /*
1924 * Block this expression for deeper evaluation.
1925 */
1926 if ((comp->flags & XML_STREAM_DESC) == 0)
1927 stream->blockLevel = stream->level +1;
1928 goto next_state;
1929 } else if (step.nodeType != XML_STREAM_ANY_NODE)
1930 goto next_state;
1931 }
1932 /*
1933 * Compare local/namespace-name.
1934 */
1935 match = 0;
1936 if (step.nodeType == XML_STREAM_ANY_NODE) {
1937 match = 1;
1938 } else if (step.name == NULL) {
1939 if (step.ns == NULL) {
1940 /*
1941 * This lets through all elements/attributes.
1942 */
1943 match = 1;
1944 } else if (ns != NULL)
1945 match = xmlStrEqual(step.ns, ns);
1946 } else if (((step.ns != NULL) == (ns != NULL)) &&
1947 (name != NULL) &&
1948 (step.name[0] == name[0]) &&
1949 xmlStrEqual(step.name, name) &&
1950 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
1951 {
1952 match = 1;
1953 }
1954#if 0
1955/*
1956* TODO: Pointer comparison won't work, since not guaranteed that the given
1957* values are in the same dict; especially if it's the namespace name,
1958* normally coming from ns->href. We need a namespace dict mechanism !
1959*/
1960 } else if (comp->dict) {
1961 if (step.name == NULL) {
1962 if (step.ns == NULL)
1963 match = 1;
1964 else
1965 match = (step.ns == ns);
1966 } else {
1967 match = ((step.name == name) && (step.ns == ns));
1968 }
1969#endif /* if 0 ------------------------------------------------------- */
1970 if (match) {
1971 final = step.flags & XML_STREAM_STEP_FINAL;
1972 if (final) {
1973 ret = 1;
1974 } else if (xmlStreamCtxtAddState(stream, stepNr + 1,
1975 stream->level + 1) < 0) {
1976 return(-1);
1977 }
1978 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
1979 /*
1980 * Check if we have a special case like "foo/bar//.", where
1981 * "foo" is selected as well.
1982 */
1983 ret = 1;
1984 }
1985 }
1986 if (((comp->flags & XML_STREAM_DESC) == 0) &&
1987 ((! match) || final)) {
1988 /*
1989 * Mark this expression as blocked for any evaluation at
1990 * deeper levels. Note that this includes "/foo"
1991 * expressions if the *pattern* behaviour is used.
1992 */
1993 stream->blockLevel = stream->level +1;
1994 }
1995next_state:
1996 i++;
1997 }
1998
1999 stream->level++;
2000
2001 /*
2002 * Re/enter the expression.
2003 * Don't reenter if it's an absolute expression like "/foo",
2004 * except "//foo".
2005 */
2006 step = comp->steps[0];
2007 if (step.flags & XML_STREAM_STEP_ROOT)
2008 goto stream_next;
2009
2010 desc = step.flags & XML_STREAM_STEP_DESC;
2011 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2012 /*
2013 * Re/enter the expression if it is a "descendant" one,
2014 * or if we are at the 1st level of evaluation.
2015 */
2016
2017 if (stream->level == 1) {
2018 if (XML_STREAM_XS_IDC(stream)) {
2019 /*
2020 * XS-IDC: The missing "self::node()" will always
2021 * match the first given node.
2022 */
2023 goto stream_next;
2024 } else
2025 goto compare;
2026 }
2027 /*
2028 * A "//" is always reentrant.
2029 */
2030 if (desc)
2031 goto compare;
2032
2033 /*
2034 * XS-IDC: Process the 2nd level, since the missing
2035 * "self::node()" is responsible for the 2nd level being
2036 * the real start level.
2037 */
2038 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2039 goto compare;
2040
2041 goto stream_next;
2042 }
2043
2044compare:
2045 /*
2046 * Check expected node-type.
2047 */
2048 if (step.nodeType != nodeType) {
2049 if (nodeType == XML_ATTRIBUTE_NODE)
2050 goto stream_next;
2051 else if (step.nodeType != XML_STREAM_ANY_NODE)
2052 goto stream_next;
2053 }
2054 /*
2055 * Compare local/namespace-name.
2056 */
2057 match = 0;
2058 if (step.nodeType == XML_STREAM_ANY_NODE) {
2059 match = 1;
2060 } else if (step.name == NULL) {
2061 if (step.ns == NULL) {
2062 /*
2063 * This lets through all elements/attributes.
2064 */
2065 match = 1;
2066 } else if (ns != NULL)
2067 match = xmlStrEqual(step.ns, ns);
2068 } else if (((step.ns != NULL) == (ns != NULL)) &&
2069 (name != NULL) &&
2070 (step.name[0] == name[0]) &&
2071 xmlStrEqual(step.name, name) &&
2072 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2073 {
2074 match = 1;
2075 }
2076 final = step.flags & XML_STREAM_STEP_FINAL;
2077 if (match) {
2078 if (final) {
2079 ret = 1;
2080 } else if (xmlStreamCtxtAddState(stream, 1, stream->level) < 0) {
2081 return(-1);
2082 }
2083 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2084 /*
2085 * Check if we have a special case like "foo//.", where
2086 * "foo" is selected as well.
2087 */
2088 ret = 1;
2089 }
2090 }
2091 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2092 ((! match) || final)) {
2093 /*
2094 * Mark this expression as blocked for any evaluation at
2095 * deeper levels.
2096 */
2097 stream->blockLevel = stream->level;
2098 }
2099
2100stream_next:
2101 stream = stream->next;
2102 } /* while stream != NULL */
2103
2104 return(ret);
2105}
2106
2107/**
2108 * xmlStreamPush:
2109 * @stream: the stream context
2110 * @name: the current name
2111 * @ns: the namespace name
2112 *
2113 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2114 * indicated a dictionary, then strings for name and ns will be expected
2115 * to come from the dictionary.
2116 * Both @name and @ns being NULL means the / i.e. the root of the document.
2117 * This can also act as a reset.
2118 * Otherwise the function will act as if it has been given an element-node.
2119 *
2120 * Returns: -1 in case of error, 1 if the current state in the stream is a
2121 * match and 0 otherwise.
2122 */
2123int
2124xmlStreamPush(xmlStreamCtxtPtr stream,
2125 const xmlChar *name, const xmlChar *ns) {
2126 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
2127}
2128
2129/**
2130 * xmlStreamPushNode:
2131 * @stream: the stream context
2132 * @name: the current name
2133 * @ns: the namespace name
2134 * @nodeType: the type of the node being pushed
2135 *
2136 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2137 * indicated a dictionary, then strings for name and ns will be expected
2138 * to come from the dictionary.
2139 * Both @name and @ns being NULL means the / i.e. the root of the document.
2140 * This can also act as a reset.
2141 * Different from xmlStreamPush() this function can be fed with nodes of type:
2142 * element-, attribute-, text-, cdata-section-, comment- and
2143 * processing-instruction-node.
2144 *
2145 * Returns: -1 in case of error, 1 if the current state in the stream is a
2146 * match and 0 otherwise.
2147 */
2148int
2149xmlStreamPushNode(xmlStreamCtxtPtr stream,
2150 const xmlChar *name, const xmlChar *ns,
2151 int nodeType)
2152{
2153 return (xmlStreamPushInternal(stream, name, ns,
2154 nodeType));
2155}
2156
2157/**
2158* xmlStreamPushAttr:
2159* @stream: the stream context
2160* @name: the current name
2161* @ns: the namespace name
2162*
2163* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2164* indicated a dictionary, then strings for name and ns will be expected
2165* to come from the dictionary.
2166* Both @name and @ns being NULL means the / i.e. the root of the document.
2167* This can also act as a reset.
2168* Otherwise the function will act as if it has been given an attribute-node.
2169*
2170* Returns: -1 in case of error, 1 if the current state in the stream is a
2171* match and 0 otherwise.
2172*/
2173int
2174xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2175 const xmlChar *name, const xmlChar *ns) {
2176 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
2177}
2178
2179/**
2180 * xmlStreamPop:
2181 * @stream: the stream context
2182 *
2183 * push one level from the stream.
2184 *
2185 * Returns: -1 in case of error, 0 otherwise.
2186 */
2187int
2188xmlStreamPop(xmlStreamCtxtPtr stream) {
2189 int i, lev;
2190
2191 if (stream == NULL)
2192 return(-1);
2193 while (stream != NULL) {
2194 /*
2195 * Reset block-level.
2196 */
2197 if (stream->blockLevel == stream->level)
2198 stream->blockLevel = -1;
2199
2200 /*
2201 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2202 * (see the thread at
2203 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2204 */
2205 if (stream->level)
2206 stream->level--;
2207 /*
2208 * Check evolution of existing states
2209 */
2210 for (i = stream->nbState -1; i >= 0; i--) {
2211 /* discard obsoleted states */
2212 lev = stream->states[(2 * i) + 1];
2213 if (lev > stream->level)
2214 stream->nbState--;
2215 if (lev <= stream->level)
2216 break;
2217 }
2218 stream = stream->next;
2219 }
2220 return(0);
2221}
2222
2223/**
2224 * xmlStreamWantsAnyNode:
2225 * @streamCtxt: the stream context
2226 *
2227 * Query if the streaming pattern additionally needs to be fed with
2228 * text-, cdata-section-, comment- and processing-instruction-nodes.
2229 * If the result is 0 then only element-nodes and attribute-nodes
2230 * need to be pushed.
2231 *
2232 * Returns: 1 in case of need of nodes of the above described types,
2233 * 0 otherwise. -1 on API errors.
2234 */
2235int
2236xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2237{
2238 if (streamCtxt == NULL)
2239 return(-1);
2240 while (streamCtxt != NULL) {
2241 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2242 return(1);
2243 streamCtxt = streamCtxt->next;
2244 }
2245 return(0);
2246}
2247
2248/************************************************************************
2249 * *
2250 * The public interfaces *
2251 * *
2252 ************************************************************************/
2253
2254/**
2255 * xmlPatternCompileSafe:
2256 * @pattern: the pattern to compile
2257 * @dict: an optional dictionary for interned strings
2258 * @flags: compilation flags, see xmlPatternFlags
2259 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2260 * @patternOut: output pattern
2261 *
2262 * Compile a pattern.
2263 *
2264 * Available since 2.13.0.
2265 *
2266 * Returns 0 on success, 1 on error, -1 if a memory allocation failed.
2267 */
2268int
2269xmlPatternCompileSafe(const xmlChar *pattern, xmlDict *dict, int flags,
2270 const xmlChar **namespaces, xmlPatternPtr *patternOut) {
2271 xmlPatternPtr ret = NULL, cur;
2272 xmlPatParserContextPtr ctxt = NULL;
2273 const xmlChar *or, *start;
2274 xmlChar *tmp = NULL;
2275 int type = 0;
2276 int streamable = 1;
2277 int error;
2278
2279 if (patternOut == NULL)
2280 return(1);
2281
2282 if (pattern == NULL) {
2283 error = 1;
2284 goto error;
2285 }
2286
2287 start = pattern;
2288 or = start;
2289 while (*or != 0) {
2290 tmp = NULL;
2291 while ((*or != 0) && (*or != '|')) or++;
2292 if (*or == 0)
2293 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2294 else {
2295 tmp = xmlStrndup(start, or - start);
2296 if (tmp != NULL) {
2297 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2298 }
2299 or++;
2300 }
2301 if (ctxt == NULL) {
2302 error = -1;
2303 goto error;
2304 }
2305 cur = xmlNewPattern();
2306 if (cur == NULL) {
2307 error = -1;
2308 goto error;
2309 }
2310 /*
2311 * Assign string dict.
2312 */
2313 if (dict) {
2314 cur->dict = dict;
2315 xmlDictReference(dict);
2316 }
2317 if (ret == NULL)
2318 ret = cur;
2319 else {
2320 cur->next = ret->next;
2321 ret->next = cur;
2322 }
2323 cur->flags = flags;
2324 ctxt->comp = cur;
2325
2326 if (XML_STREAM_XS_IDC(cur))
2327 xmlCompileIDCXPathPath(ctxt);
2328 else
2329 xmlCompilePathPattern(ctxt);
2330 if (ctxt->error != 0) {
2331 error = ctxt->error;
2332 goto error;
2333 }
2334 xmlFreePatParserContext(ctxt);
2335 ctxt = NULL;
2336
2337
2338 if (streamable) {
2339 if (type == 0) {
2340 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2341 } else if (type == PAT_FROM_ROOT) {
2342 if (cur->flags & PAT_FROM_CUR)
2343 streamable = 0;
2344 } else if (type == PAT_FROM_CUR) {
2345 if (cur->flags & PAT_FROM_ROOT)
2346 streamable = 0;
2347 }
2348 }
2349 if (streamable) {
2350 error = xmlStreamCompile(cur);
2351 if (error != 0)
2352 goto error;
2353 }
2354 error = xmlReversePattern(cur);
2355 if (error != 0)
2356 goto error;
2357 if (tmp != NULL) {
2358 xmlFree(tmp);
2359 tmp = NULL;
2360 }
2361 start = or;
2362 }
2363 if (streamable == 0) {
2364 cur = ret;
2365 while (cur != NULL) {
2366 if (cur->stream != NULL) {
2367 xmlFreeStreamComp(cur->stream);
2368 cur->stream = NULL;
2369 }
2370 cur = cur->next;
2371 }
2372 }
2373
2374 *patternOut = ret;
2375 return(0);
2376error:
2377 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2378 if (ret != NULL) xmlFreePattern(ret);
2379 if (tmp != NULL) xmlFree(tmp);
2380 *patternOut = NULL;
2381 return(error);
2382}
2383
2384/**
2385 * xmlPatterncompile:
2386 * @pattern: the pattern to compile
2387 * @dict: an optional dictionary for interned strings
2388 * @flags: compilation flags, see xmlPatternFlags
2389 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2390 *
2391 * Compile a pattern.
2392 *
2393 * Returns the compiled form of the pattern or NULL in case of error
2394 */
2395xmlPatternPtr
2396xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2397 const xmlChar **namespaces) {
2398 xmlPatternPtr ret;
2399 xmlPatternCompileSafe(pattern, dict, flags, namespaces, &ret);
2400 return(ret);
2401}
2402
2403/**
2404 * xmlPatternMatch:
2405 * @comp: the precompiled pattern
2406 * @node: a node
2407 *
2408 * Test whether the node matches the pattern
2409 *
2410 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2411 */
2412int
2413xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2414{
2415 int ret = 0;
2416
2417 if ((comp == NULL) || (node == NULL))
2418 return(-1);
2419
2420 while (comp != NULL) {
2421 ret = xmlPatMatch(comp, node);
2422 if (ret != 0)
2423 return(ret);
2424 comp = comp->next;
2425 }
2426 return(ret);
2427}
2428
2429/**
2430 * xmlPatternGetStreamCtxt:
2431 * @comp: the precompiled pattern
2432 *
2433 * Get a streaming context for that pattern
2434 * Use xmlFreeStreamCtxt to free the context.
2435 *
2436 * Returns a pointer to the context or NULL in case of failure
2437 */
2438xmlStreamCtxtPtr
2439xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2440{
2441 xmlStreamCtxtPtr ret = NULL, cur;
2442
2443 if ((comp == NULL) || (comp->stream == NULL))
2444 return(NULL);
2445
2446 while (comp != NULL) {
2447 if (comp->stream == NULL)
2448 goto failed;
2449 cur = xmlNewStreamCtxt(comp->stream);
2450 if (cur == NULL)
2451 goto failed;
2452 if (ret == NULL)
2453 ret = cur;
2454 else {
2455 cur->next = ret->next;
2456 ret->next = cur;
2457 }
2458 cur->flags = comp->flags;
2459 comp = comp->next;
2460 }
2461 return(ret);
2462failed:
2463 xmlFreeStreamCtxt(ret);
2464 return(NULL);
2465}
2466
2467/**
2468 * xmlPatternStreamable:
2469 * @comp: the precompiled pattern
2470 *
2471 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2472 * should work.
2473 *
2474 * Returns 1 if streamable, 0 if not and -1 in case of error.
2475 */
2476int
2477xmlPatternStreamable(xmlPatternPtr comp) {
2478 if (comp == NULL)
2479 return(-1);
2480 while (comp != NULL) {
2481 if (comp->stream == NULL)
2482 return(0);
2483 comp = comp->next;
2484 }
2485 return(1);
2486}
2487
2488/**
2489 * xmlPatternMaxDepth:
2490 * @comp: the precompiled pattern
2491 *
2492 * Check the maximum depth reachable by a pattern
2493 *
2494 * Returns -2 if no limit (using //), otherwise the depth,
2495 * and -1 in case of error
2496 */
2497int
2498xmlPatternMaxDepth(xmlPatternPtr comp) {
2499 int ret = 0, i;
2500 if (comp == NULL)
2501 return(-1);
2502 while (comp != NULL) {
2503 if (comp->stream == NULL)
2504 return(-1);
2505 for (i = 0;i < comp->stream->nbStep;i++)
2506 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2507 return(-2);
2508 if (comp->stream->nbStep > ret)
2509 ret = comp->stream->nbStep;
2510 comp = comp->next;
2511 }
2512 return(ret);
2513}
2514
2515/**
2516 * xmlPatternMinDepth:
2517 * @comp: the precompiled pattern
2518 *
2519 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2520 * part of the set.
2521 *
2522 * Returns -1 in case of error otherwise the depth,
2523 *
2524 */
2525int
2526xmlPatternMinDepth(xmlPatternPtr comp) {
2527 int ret = 12345678;
2528 if (comp == NULL)
2529 return(-1);
2530 while (comp != NULL) {
2531 if (comp->stream == NULL)
2532 return(-1);
2533 if (comp->stream->nbStep < ret)
2534 ret = comp->stream->nbStep;
2535 if (ret == 0)
2536 return(0);
2537 comp = comp->next;
2538 }
2539 return(ret);
2540}
2541
2542/**
2543 * xmlPatternFromRoot:
2544 * @comp: the precompiled pattern
2545 *
2546 * Check if the pattern must be looked at from the root.
2547 *
2548 * Returns 1 if true, 0 if false and -1 in case of error
2549 */
2550int
2551xmlPatternFromRoot(xmlPatternPtr comp) {
2552 if (comp == NULL)
2553 return(-1);
2554 while (comp != NULL) {
2555 if (comp->stream == NULL)
2556 return(-1);
2557 if (comp->flags & PAT_FROM_ROOT)
2558 return(1);
2559 comp = comp->next;
2560 }
2561 return(0);
2562
2563}
2564
2565#endif /* LIBXML_PATTERN_ENABLED */
Note: See TracBrowser for help on using the repository browser.

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