VirtualBox

source: vbox/trunk/src/libs/libxml2-2.12.6/pattern.c

Last change on this file was 104106, checked in by vboxsync, 2 months ago

libxml2-2.9.14: Applied and adjusted our libxml2 changes to 2.9.14. bugref:10640

  • Property svn:eol-style set to native
File size: 60.0 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 ATTRIBUTE_UNUSED,
348 xmlPatternPtr comp,
349 xmlPatOp op, xmlChar * value, xmlChar * value2)
350{
351 if (comp->nbStep >= comp->maxStep) {
352 xmlStepOpPtr temp;
353 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
354 sizeof(xmlStepOp));
355 if (temp == NULL) {
356 ERROR(ctxt, NULL, NULL,
357 "xmlPatternAdd: realloc failed\n");
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 TODO \
701 xmlGenericError(xmlGenericErrorContext, \
702 "Unimplemented block at %s:%d\n", \
703 __FILE__, __LINE__);
704#define CUR (*ctxt->cur)
705#define SKIP(val) ctxt->cur += (val)
706#define NXT(val) ctxt->cur[(val)]
707#define PEEKPREV(val) ctxt->cur[-(val)]
708#define CUR_PTR ctxt->cur
709
710#define SKIP_BLANKS \
711 while (IS_BLANK_CH(CUR)) NEXT
712
713#define CURRENT (*ctxt->cur)
714#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
715
716
717#define PUSH(op, val, val2) \
718 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
719
720#define XSLT_ERROR(X) \
721 { xsltError(ctxt, __FILE__, __LINE__, X); \
722 ctxt->error = (X); return; }
723
724#define XSLT_ERROR0(X) \
725 { xsltError(ctxt, __FILE__, __LINE__, X); \
726 ctxt->error = (X); return(0); }
727
728#if 0
729/**
730 * xmlPatScanLiteral:
731 * @ctxt: the XPath Parser context
732 *
733 * Parse an XPath Literal:
734 *
735 * [29] Literal ::= '"' [^"]* '"'
736 * | "'" [^']* "'"
737 *
738 * Returns the Literal parsed or NULL
739 */
740
741static xmlChar *
742xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
743 const xmlChar *q, *cur;
744 xmlChar *ret = NULL;
745 int val, len;
746
747 SKIP_BLANKS;
748 if (CUR == '"') {
749 NEXT;
750 cur = q = CUR_PTR;
751 val = xmlStringCurrentChar(NULL, cur, &len);
752 while ((IS_CHAR(val)) && (val != '"')) {
753 cur += len;
754 val = xmlStringCurrentChar(NULL, cur, &len);
755 }
756 if (!IS_CHAR(val)) {
757 ctxt->error = 1;
758 return(NULL);
759 } else {
760 if (ctxt->dict)
761 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
762 else
763 ret = xmlStrndup(q, cur - q);
764 }
765 cur += len;
766 CUR_PTR = cur;
767 } else if (CUR == '\'') {
768 NEXT;
769 cur = q = CUR_PTR;
770 val = xmlStringCurrentChar(NULL, cur, &len);
771 while ((IS_CHAR(val)) && (val != '\'')) {
772 cur += len;
773 val = xmlStringCurrentChar(NULL, cur, &len);
774 }
775 if (!IS_CHAR(val)) {
776 ctxt->error = 1;
777 return(NULL);
778 } else {
779 if (ctxt->dict)
780 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
781 else
782 ret = xmlStrndup(q, cur - q);
783 }
784 cur += len;
785 CUR_PTR = cur;
786 } else {
787 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
788 ctxt->error = 1;
789 return(NULL);
790 }
791 return(ret);
792}
793#endif
794
795/**
796 * xmlPatScanName:
797 * @ctxt: the XPath Parser context
798 *
799 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
800 * CombiningChar | Extender
801 *
802 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
803 *
804 * [6] Names ::= Name (S Name)*
805 *
806 * Returns the Name parsed or NULL
807 */
808
809static xmlChar *
810xmlPatScanName(xmlPatParserContextPtr ctxt) {
811 const xmlChar *q, *cur;
812 xmlChar *ret = NULL;
813 int val, len;
814
815 SKIP_BLANKS;
816
817 cur = q = CUR_PTR;
818 val = xmlStringCurrentChar(NULL, cur, &len);
819 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
820 return(NULL);
821
822 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
823 (val == '.') || (val == '-') ||
824 (val == '_') ||
825 (IS_COMBINING(val)) ||
826 (IS_EXTENDER(val))) {
827 cur += len;
828 val = xmlStringCurrentChar(NULL, cur, &len);
829 }
830 if (ctxt->dict)
831 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
832 else
833 ret = xmlStrndup(q, cur - q);
834 CUR_PTR = cur;
835 return(ret);
836}
837
838/**
839 * xmlPatScanNCName:
840 * @ctxt: the XPath Parser context
841 *
842 * Parses a non qualified name
843 *
844 * Returns the Name parsed or NULL
845 */
846
847static xmlChar *
848xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
849 const xmlChar *q, *cur;
850 xmlChar *ret = NULL;
851 int val, len;
852
853 SKIP_BLANKS;
854
855 cur = q = CUR_PTR;
856 val = xmlStringCurrentChar(NULL, cur, &len);
857 if (!IS_LETTER(val) && (val != '_'))
858 return(NULL);
859
860 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
861 (val == '.') || (val == '-') ||
862 (val == '_') ||
863 (IS_COMBINING(val)) ||
864 (IS_EXTENDER(val))) {
865 cur += len;
866 val = xmlStringCurrentChar(NULL, cur, &len);
867 }
868 if (ctxt->dict)
869 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
870 else
871 ret = xmlStrndup(q, cur - q);
872 CUR_PTR = cur;
873 return(ret);
874}
875
876#if 0
877/**
878 * xmlPatScanQName:
879 * @ctxt: the XPath Parser context
880 * @prefix: the place to store the prefix
881 *
882 * Parse a qualified name
883 *
884 * Returns the Name parsed or NULL
885 */
886
887static xmlChar *
888xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
889 xmlChar *ret = NULL;
890
891 *prefix = NULL;
892 ret = xmlPatScanNCName(ctxt);
893 if (CUR == ':') {
894 *prefix = ret;
895 NEXT;
896 ret = xmlPatScanNCName(ctxt);
897 }
898 return(ret);
899}
900#endif
901
902/**
903 * xmlCompileAttributeTest:
904 * @ctxt: the compilation context
905 *
906 * Compile an attribute test.
907 */
908static void
909xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
910 xmlChar *token = NULL;
911 xmlChar *name = NULL;
912 xmlChar *URL = NULL;
913
914 SKIP_BLANKS;
915 name = xmlPatScanNCName(ctxt);
916 if (name == NULL) {
917 if (CUR == '*') {
918 PUSH(XML_OP_ATTR, NULL, NULL);
919 NEXT;
920 } else {
921 ERROR(NULL, NULL, NULL,
922 "xmlCompileAttributeTest : Name expected\n");
923 ctxt->error = 1;
924 }
925 return;
926 }
927 if (CUR == ':') {
928 int i;
929 xmlChar *prefix = name;
930
931 NEXT;
932
933 if (IS_BLANK_CH(CUR)) {
934 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
935 ctxt->error = 1;
936 goto error;
937 }
938 /*
939 * This is a namespace match
940 */
941 token = xmlPatScanName(ctxt);
942 if ((prefix[0] == 'x') &&
943 (prefix[1] == 'm') &&
944 (prefix[2] == 'l') &&
945 (prefix[3] == 0))
946 {
947 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
948 } else {
949 for (i = 0;i < ctxt->nb_namespaces;i++) {
950 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
951 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
952 break;
953 }
954 }
955 if (i >= ctxt->nb_namespaces) {
956 ERROR5(NULL, NULL, NULL,
957 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
958 prefix);
959 ctxt->error = 1;
960 goto error;
961 }
962 }
963 XML_PAT_FREE_STRING(ctxt, name);
964 name = NULL;
965 if (token == NULL) {
966 if (CUR == '*') {
967 NEXT;
968 PUSH(XML_OP_ATTR, NULL, URL);
969 } else {
970 ERROR(NULL, NULL, NULL,
971 "xmlCompileAttributeTest : Name expected\n");
972 ctxt->error = 1;
973 goto error;
974 }
975 } else {
976 PUSH(XML_OP_ATTR, token, URL);
977 }
978 } else {
979 PUSH(XML_OP_ATTR, name, NULL);
980 }
981 return;
982error:
983 if (name != NULL)
984 XML_PAT_FREE_STRING(ctxt, name);
985 if (URL != NULL)
986 XML_PAT_FREE_STRING(ctxt, URL)
987 if (token != NULL)
988 XML_PAT_FREE_STRING(ctxt, token);
989}
990
991/**
992 * xmlCompileStepPattern:
993 * @ctxt: the compilation context
994 *
995 * Compile the Step Pattern and generates a precompiled
996 * form suitable for fast matching.
997 *
998 * [3] Step ::= '.' | NameTest
999 * [4] NameTest ::= QName | '*' | NCName ':' '*'
1000 */
1001
1002static void
1003xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1004 xmlChar *token = NULL;
1005 xmlChar *name = NULL;
1006 xmlChar *URL = NULL;
1007 int hasBlanks = 0;
1008
1009 SKIP_BLANKS;
1010 if (CUR == '.') {
1011 /*
1012 * Context node.
1013 */
1014 NEXT;
1015 PUSH(XML_OP_ELEM, NULL, NULL);
1016 return;
1017 }
1018 if (CUR == '@') {
1019 /*
1020 * Attribute test.
1021 */
1022 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1023 ERROR5(NULL, NULL, NULL,
1024 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1025 ctxt->error = 1;
1026 return;
1027 }
1028 NEXT;
1029 xmlCompileAttributeTest(ctxt);
1030 if (ctxt->error != 0)
1031 goto error;
1032 return;
1033 }
1034 name = xmlPatScanNCName(ctxt);
1035 if (name == NULL) {
1036 if (CUR == '*') {
1037 NEXT;
1038 PUSH(XML_OP_ALL, NULL, NULL);
1039 return;
1040 } else {
1041 ERROR(NULL, NULL, NULL,
1042 "xmlCompileStepPattern : Name expected\n");
1043 ctxt->error = 1;
1044 return;
1045 }
1046 }
1047 if (IS_BLANK_CH(CUR)) {
1048 hasBlanks = 1;
1049 SKIP_BLANKS;
1050 }
1051 if (CUR == ':') {
1052 NEXT;
1053 if (CUR != ':') {
1054 xmlChar *prefix = name;
1055 int i;
1056
1057 if (hasBlanks || IS_BLANK_CH(CUR)) {
1058 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1059 ctxt->error = 1;
1060 goto error;
1061 }
1062 /*
1063 * This is a namespace match
1064 */
1065 token = xmlPatScanName(ctxt);
1066 if ((prefix[0] == 'x') &&
1067 (prefix[1] == 'm') &&
1068 (prefix[2] == 'l') &&
1069 (prefix[3] == 0))
1070 {
1071 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1072 } else {
1073 for (i = 0;i < ctxt->nb_namespaces;i++) {
1074 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1075 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1076 break;
1077 }
1078 }
1079 if (i >= ctxt->nb_namespaces) {
1080 ERROR5(NULL, NULL, NULL,
1081 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1082 prefix);
1083 ctxt->error = 1;
1084 goto error;
1085 }
1086 }
1087 XML_PAT_FREE_STRING(ctxt, prefix);
1088 name = NULL;
1089 if (token == NULL) {
1090 if (CUR == '*') {
1091 NEXT;
1092 PUSH(XML_OP_NS, URL, NULL);
1093 } else {
1094 ERROR(NULL, NULL, NULL,
1095 "xmlCompileStepPattern : Name expected\n");
1096 ctxt->error = 1;
1097 goto error;
1098 }
1099 } else {
1100 PUSH(XML_OP_ELEM, token, URL);
1101 }
1102 } else {
1103 NEXT;
1104 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1105 XML_PAT_FREE_STRING(ctxt, name);
1106 name = xmlPatScanName(ctxt);
1107 if (name == NULL) {
1108 if (CUR == '*') {
1109 NEXT;
1110 PUSH(XML_OP_ALL, NULL, NULL);
1111 return;
1112 } else {
1113 ERROR(NULL, NULL, NULL,
1114 "xmlCompileStepPattern : QName expected\n");
1115 ctxt->error = 1;
1116 goto error;
1117 }
1118 }
1119 if (CUR == ':') {
1120 xmlChar *prefix = name;
1121 int i;
1122
1123 NEXT;
1124 if (IS_BLANK_CH(CUR)) {
1125 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1126 ctxt->error = 1;
1127 goto error;
1128 }
1129 /*
1130 * This is a namespace match
1131 */
1132 token = xmlPatScanName(ctxt);
1133 if ((prefix[0] == 'x') &&
1134 (prefix[1] == 'm') &&
1135 (prefix[2] == 'l') &&
1136 (prefix[3] == 0))
1137 {
1138 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1139 } else {
1140 for (i = 0;i < ctxt->nb_namespaces;i++) {
1141 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1142 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1143 break;
1144 }
1145 }
1146 if (i >= ctxt->nb_namespaces) {
1147 ERROR5(NULL, NULL, NULL,
1148 "xmlCompileStepPattern : no namespace bound "
1149 "to prefix %s\n", prefix);
1150 ctxt->error = 1;
1151 goto error;
1152 }
1153 }
1154 XML_PAT_FREE_STRING(ctxt, prefix);
1155 name = NULL;
1156 if (token == NULL) {
1157 if (CUR == '*') {
1158 NEXT;
1159 PUSH(XML_OP_NS, URL, NULL);
1160 } else {
1161 ERROR(NULL, NULL, NULL,
1162 "xmlCompileStepPattern : Name expected\n");
1163 ctxt->error = 1;
1164 goto error;
1165 }
1166 } else {
1167 PUSH(XML_OP_CHILD, token, URL);
1168 }
1169 } else
1170 PUSH(XML_OP_CHILD, name, NULL);
1171 return;
1172 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1173 XML_PAT_FREE_STRING(ctxt, name)
1174 name = NULL;
1175 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1176 ERROR5(NULL, NULL, NULL,
1177 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1178 ctxt->error = 1;
1179 goto error;
1180 }
1181 xmlCompileAttributeTest(ctxt);
1182 if (ctxt->error != 0)
1183 goto error;
1184 return;
1185 } else {
1186 ERROR5(NULL, NULL, NULL,
1187 "The 'element' or 'attribute' axis is expected.\n", NULL);
1188 ctxt->error = 1;
1189 goto error;
1190 }
1191 }
1192 } else if (CUR == '*') {
1193 if (name != NULL) {
1194 ctxt->error = 1;
1195 goto error;
1196 }
1197 NEXT;
1198 PUSH(XML_OP_ALL, token, NULL);
1199 } else {
1200 PUSH(XML_OP_ELEM, name, NULL);
1201 }
1202 return;
1203error:
1204 if (URL != NULL)
1205 XML_PAT_FREE_STRING(ctxt, URL)
1206 if (token != NULL)
1207 XML_PAT_FREE_STRING(ctxt, token)
1208 if (name != NULL)
1209 XML_PAT_FREE_STRING(ctxt, name)
1210}
1211
1212/**
1213 * xmlCompilePathPattern:
1214 * @ctxt: the compilation context
1215 *
1216 * Compile the Path Pattern and generates a precompiled
1217 * form suitable for fast matching.
1218 *
1219 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1220 */
1221static void
1222xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1223 SKIP_BLANKS;
1224 if (CUR == '/') {
1225 ctxt->comp->flags |= PAT_FROM_ROOT;
1226 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1227 ctxt->comp->flags |= PAT_FROM_CUR;
1228 }
1229
1230 if ((CUR == '/') && (NXT(1) == '/')) {
1231 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1232 NEXT;
1233 NEXT;
1234 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1235 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1236 NEXT;
1237 NEXT;
1238 NEXT;
1239 /* Check for incompleteness. */
1240 SKIP_BLANKS;
1241 if (CUR == 0) {
1242 ERROR5(NULL, NULL, NULL,
1243 "Incomplete expression '%s'.\n", ctxt->base);
1244 ctxt->error = 1;
1245 goto error;
1246 }
1247 }
1248 if (CUR == '@') {
1249 NEXT;
1250 xmlCompileAttributeTest(ctxt);
1251 SKIP_BLANKS;
1252 /* TODO: check for incompleteness */
1253 if (CUR != 0) {
1254 xmlCompileStepPattern(ctxt);
1255 if (ctxt->error != 0)
1256 goto error;
1257 }
1258 } else {
1259 if (CUR == '/') {
1260 PUSH(XML_OP_ROOT, NULL, NULL);
1261 NEXT;
1262 /* Check for incompleteness. */
1263 SKIP_BLANKS;
1264 if (CUR == 0) {
1265 ERROR5(NULL, NULL, NULL,
1266 "Incomplete expression '%s'.\n", ctxt->base);
1267 ctxt->error = 1;
1268 goto error;
1269 }
1270 }
1271 xmlCompileStepPattern(ctxt);
1272 if (ctxt->error != 0)
1273 goto error;
1274 SKIP_BLANKS;
1275 while (CUR == '/') {
1276 if (NXT(1) == '/') {
1277 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1278 NEXT;
1279 NEXT;
1280 SKIP_BLANKS;
1281 xmlCompileStepPattern(ctxt);
1282 if (ctxt->error != 0)
1283 goto error;
1284 } else {
1285 PUSH(XML_OP_PARENT, NULL, NULL);
1286 NEXT;
1287 SKIP_BLANKS;
1288 if (CUR == 0) {
1289 ERROR5(NULL, NULL, NULL,
1290 "Incomplete expression '%s'.\n", ctxt->base);
1291 ctxt->error = 1;
1292 goto error;
1293 }
1294 xmlCompileStepPattern(ctxt);
1295 if (ctxt->error != 0)
1296 goto error;
1297 }
1298 }
1299 }
1300 if (CUR != 0) {
1301 ERROR5(NULL, NULL, NULL,
1302 "Failed to compile pattern %s\n", ctxt->base);
1303 ctxt->error = 1;
1304 }
1305error:
1306 return;
1307}
1308
1309/**
1310 * xmlCompileIDCXPathPath:
1311 * @ctxt: the compilation context
1312 *
1313 * Compile the Path Pattern and generates a precompiled
1314 * form suitable for fast matching.
1315 *
1316 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1317 */
1318static void
1319xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1320 SKIP_BLANKS;
1321 if (CUR == '/') {
1322 ERROR5(NULL, NULL, NULL,
1323 "Unexpected selection of the document root in '%s'.\n",
1324 ctxt->base);
1325 goto error;
1326 }
1327 ctxt->comp->flags |= PAT_FROM_CUR;
1328
1329 if (CUR == '.') {
1330 /* "." - "self::node()" */
1331 NEXT;
1332 SKIP_BLANKS;
1333 if (CUR == 0) {
1334 /*
1335 * Selection of the context node.
1336 */
1337 PUSH(XML_OP_ELEM, NULL, NULL);
1338 return;
1339 }
1340 if (CUR != '/') {
1341 /* TODO: A more meaningful error message. */
1342 ERROR5(NULL, NULL, NULL,
1343 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1344 goto error;
1345 }
1346 /* "./" - "self::node()/" */
1347 NEXT;
1348 SKIP_BLANKS;
1349 if (CUR == '/') {
1350 if (IS_BLANK_CH(PEEKPREV(1))) {
1351 /*
1352 * Disallow "./ /"
1353 */
1354 ERROR5(NULL, NULL, NULL,
1355 "Unexpected '/' token in '%s'.\n", ctxt->base);
1356 goto error;
1357 }
1358 /* ".//" - "self:node()/descendant-or-self::node()/" */
1359 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1360 NEXT;
1361 SKIP_BLANKS;
1362 }
1363 if (CUR == 0)
1364 goto error_unfinished;
1365 }
1366 /*
1367 * Process steps.
1368 */
1369 do {
1370 xmlCompileStepPattern(ctxt);
1371 if (ctxt->error != 0)
1372 goto error;
1373 SKIP_BLANKS;
1374 if (CUR != '/')
1375 break;
1376 PUSH(XML_OP_PARENT, NULL, NULL);
1377 NEXT;
1378 SKIP_BLANKS;
1379 if (CUR == '/') {
1380 /*
1381 * Disallow subsequent '//'.
1382 */
1383 ERROR5(NULL, NULL, NULL,
1384 "Unexpected subsequent '//' in '%s'.\n",
1385 ctxt->base);
1386 goto error;
1387 }
1388 if (CUR == 0)
1389 goto error_unfinished;
1390
1391 } while (CUR != 0);
1392
1393 if (CUR != 0) {
1394 ERROR5(NULL, NULL, NULL,
1395 "Failed to compile expression '%s'.\n", ctxt->base);
1396 ctxt->error = 1;
1397 }
1398 return;
1399error:
1400 ctxt->error = 1;
1401 return;
1402
1403error_unfinished:
1404 ctxt->error = 1;
1405 ERROR5(NULL, NULL, NULL,
1406 "Unfinished expression '%s'.\n", ctxt->base);
1407 return;
1408}
1409
1410/************************************************************************
1411 * *
1412 * The streaming code *
1413 * *
1414 ************************************************************************/
1415
1416/**
1417 * xmlNewStreamComp:
1418 * @size: the number of expected steps
1419 *
1420 * build a new compiled pattern for streaming
1421 *
1422 * Returns the new structure or NULL in case of error.
1423 */
1424static xmlStreamCompPtr
1425xmlNewStreamComp(int size) {
1426 xmlStreamCompPtr cur;
1427
1428 if (size < 4)
1429 size = 4;
1430
1431 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1432 if (cur == NULL) {
1433 ERROR(NULL, NULL, NULL,
1434 "xmlNewStreamComp: malloc failed\n");
1435 return(NULL);
1436 }
1437 memset(cur, 0, sizeof(xmlStreamComp));
1438 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1439 if (cur->steps == NULL) {
1440 xmlFree(cur);
1441 ERROR(NULL, NULL, NULL,
1442 "xmlNewStreamComp: malloc failed\n");
1443 return(NULL);
1444 }
1445 cur->nbStep = 0;
1446 cur->maxStep = size;
1447 return(cur);
1448}
1449
1450/**
1451 * xmlFreeStreamComp:
1452 * @comp: the compiled pattern for streaming
1453 *
1454 * Free the compiled pattern for streaming
1455 */
1456static void
1457xmlFreeStreamComp(xmlStreamCompPtr comp) {
1458 if (comp != NULL) {
1459 if (comp->steps != NULL)
1460 xmlFree(comp->steps);
1461 if (comp->dict != NULL)
1462 xmlDictFree(comp->dict);
1463 xmlFree(comp);
1464 }
1465}
1466
1467/**
1468 * xmlStreamCompAddStep:
1469 * @comp: the compiled pattern for streaming
1470 * @name: the first string, the name, or NULL for *
1471 * @ns: the second step, the namespace name
1472 * @flags: the flags for that step
1473 *
1474 * Add a new step to the compiled pattern
1475 *
1476 * Returns -1 in case of error or the step index if successful
1477 */
1478static int
1479xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1480 const xmlChar *ns, int nodeType, int flags) {
1481 xmlStreamStepPtr cur;
1482
1483 if (comp->nbStep >= comp->maxStep) {
1484 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1485 comp->maxStep * 2 * sizeof(xmlStreamStep));
1486 if (cur == NULL) {
1487 ERROR(NULL, NULL, NULL,
1488 "xmlNewStreamComp: malloc failed\n");
1489 return(-1);
1490 }
1491 comp->steps = cur;
1492 comp->maxStep *= 2;
1493 }
1494 cur = &comp->steps[comp->nbStep++];
1495 cur->flags = flags;
1496 cur->name = name;
1497 cur->ns = ns;
1498 cur->nodeType = nodeType;
1499 return(comp->nbStep - 1);
1500}
1501
1502/**
1503 * xmlStreamCompile:
1504 * @comp: the precompiled pattern
1505 *
1506 * Tries to stream compile a pattern
1507 *
1508 * Returns -1 in case of failure and 0 in case of success.
1509 */
1510static int
1511xmlStreamCompile(xmlPatternPtr comp) {
1512 xmlStreamCompPtr stream;
1513 int i, s = 0, root = 0, flags = 0, prevs = -1;
1514 xmlStepOp step;
1515
1516 if ((comp == NULL) || (comp->steps == NULL))
1517 return(-1);
1518 /*
1519 * special case for .
1520 */
1521 if ((comp->nbStep == 1) &&
1522 (comp->steps[0].op == XML_OP_ELEM) &&
1523 (comp->steps[0].value == NULL) &&
1524 (comp->steps[0].value2 == NULL)) {
1525 stream = xmlNewStreamComp(0);
1526 if (stream == NULL)
1527 return(-1);
1528 /* Note that the stream will have no steps in this case. */
1529 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1530 comp->stream = stream;
1531 return(0);
1532 }
1533
1534 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1535 if (stream == NULL)
1536 return(-1);
1537 if (comp->dict != NULL) {
1538 stream->dict = comp->dict;
1539 xmlDictReference(stream->dict);
1540 }
1541
1542 i = 0;
1543 if (comp->flags & PAT_FROM_ROOT)
1544 stream->flags |= XML_STREAM_FROM_ROOT;
1545
1546 for (;i < comp->nbStep;i++) {
1547 step = comp->steps[i];
1548 switch (step.op) {
1549 case XML_OP_END:
1550 break;
1551 case XML_OP_ROOT:
1552 if (i != 0)
1553 goto error;
1554 root = 1;
1555 break;
1556 case XML_OP_NS:
1557 s = xmlStreamCompAddStep(stream, NULL, step.value,
1558 XML_ELEMENT_NODE, flags);
1559 if (s < 0)
1560 goto error;
1561 prevs = s;
1562 flags = 0;
1563 break;
1564 case XML_OP_ATTR:
1565 flags |= XML_STREAM_STEP_ATTR;
1566 prevs = -1;
1567 s = xmlStreamCompAddStep(stream,
1568 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1569 flags = 0;
1570 if (s < 0)
1571 goto error;
1572 break;
1573 case XML_OP_ELEM:
1574 if ((step.value == NULL) && (step.value2 == NULL)) {
1575 /*
1576 * We have a "." or "self::node()" here.
1577 * Eliminate redundant self::node() tests like in "/./."
1578 * or "//./"
1579 * The only case we won't eliminate is "//.", i.e. if
1580 * self::node() is the last node test and we had
1581 * continuation somewhere beforehand.
1582 */
1583 if ((comp->nbStep == i + 1) &&
1584 (flags & XML_STREAM_STEP_DESC)) {
1585 /*
1586 * Mark the special case where the expression resolves
1587 * to any type of node.
1588 */
1589 if (comp->nbStep == i + 1) {
1590 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1591 }
1592 flags |= XML_STREAM_STEP_NODE;
1593 s = xmlStreamCompAddStep(stream, NULL, NULL,
1594 XML_STREAM_ANY_NODE, flags);
1595 if (s < 0)
1596 goto error;
1597 flags = 0;
1598 /*
1599 * If there was a previous step, mark it to be added to
1600 * the result node-set; this is needed since only
1601 * the last step will be marked as "final" and only
1602 * "final" nodes are added to the resulting set.
1603 */
1604 if (prevs != -1) {
1605 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1606 prevs = -1;
1607 }
1608 break;
1609
1610 } else {
1611 /* Just skip this one. */
1612 continue;
1613 }
1614 }
1615 /* An element node. */
1616 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1617 XML_ELEMENT_NODE, flags);
1618 if (s < 0)
1619 goto error;
1620 prevs = s;
1621 flags = 0;
1622 break;
1623 case XML_OP_CHILD:
1624 /* An element node child. */
1625 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1626 XML_ELEMENT_NODE, flags);
1627 if (s < 0)
1628 goto error;
1629 prevs = s;
1630 flags = 0;
1631 break;
1632 case XML_OP_ALL:
1633 s = xmlStreamCompAddStep(stream, NULL, NULL,
1634 XML_ELEMENT_NODE, flags);
1635 if (s < 0)
1636 goto error;
1637 prevs = s;
1638 flags = 0;
1639 break;
1640 case XML_OP_PARENT:
1641 break;
1642 case XML_OP_ANCESTOR:
1643 /* Skip redundant continuations. */
1644 if (flags & XML_STREAM_STEP_DESC)
1645 break;
1646 flags |= XML_STREAM_STEP_DESC;
1647 /*
1648 * Mark the expression as having "//".
1649 */
1650 if ((stream->flags & XML_STREAM_DESC) == 0)
1651 stream->flags |= XML_STREAM_DESC;
1652 break;
1653 }
1654 }
1655 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1656 /*
1657 * If this should behave like a real pattern, we will mark
1658 * the first step as having "//", to be reentrant on every
1659 * tree level.
1660 */
1661 if ((stream->flags & XML_STREAM_DESC) == 0)
1662 stream->flags |= XML_STREAM_DESC;
1663
1664 if (stream->nbStep > 0) {
1665 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1666 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1667 }
1668 }
1669 if (stream->nbStep <= s)
1670 goto error;
1671 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1672 if (root)
1673 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1674 comp->stream = stream;
1675 return(0);
1676error:
1677 xmlFreeStreamComp(stream);
1678 return(0);
1679}
1680
1681/**
1682 * xmlNewStreamCtxt:
1683 * @size: the number of expected states
1684 *
1685 * build a new stream context
1686 *
1687 * Returns the new structure or NULL in case of error.
1688 */
1689static xmlStreamCtxtPtr
1690xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1691 xmlStreamCtxtPtr cur;
1692
1693 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1694 if (cur == NULL) {
1695 ERROR(NULL, NULL, NULL,
1696 "xmlNewStreamCtxt: malloc failed\n");
1697 return(NULL);
1698 }
1699 memset(cur, 0, sizeof(xmlStreamCtxt));
1700 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1701 if (cur->states == NULL) {
1702 xmlFree(cur);
1703 ERROR(NULL, NULL, NULL,
1704 "xmlNewStreamCtxt: malloc failed\n");
1705 return(NULL);
1706 }
1707 cur->nbState = 0;
1708 cur->maxState = 4;
1709 cur->level = 0;
1710 cur->comp = stream;
1711 cur->blockLevel = -1;
1712 return(cur);
1713}
1714
1715/**
1716 * xmlFreeStreamCtxt:
1717 * @stream: the stream context
1718 *
1719 * Free the stream context
1720 */
1721void
1722xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1723 xmlStreamCtxtPtr next;
1724
1725 while (stream != NULL) {
1726 next = stream->next;
1727 if (stream->states != NULL)
1728 xmlFree(stream->states);
1729 xmlFree(stream);
1730 stream = next;
1731 }
1732}
1733
1734/**
1735 * xmlStreamCtxtAddState:
1736 * @comp: the stream context
1737 * @idx: the step index for that streaming state
1738 *
1739 * Add a new state to the stream context
1740 *
1741 * Returns -1 in case of error or the state index if successful
1742 */
1743static int
1744xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1745 int i;
1746 for (i = 0;i < comp->nbState;i++) {
1747 if (comp->states[2 * i] < 0) {
1748 comp->states[2 * i] = idx;
1749 comp->states[2 * i + 1] = level;
1750 return(i);
1751 }
1752 }
1753 if (comp->nbState >= comp->maxState) {
1754 int *cur;
1755
1756 cur = (int *) xmlRealloc(comp->states,
1757 comp->maxState * 4 * sizeof(int));
1758 if (cur == NULL) {
1759 ERROR(NULL, NULL, NULL,
1760 "xmlNewStreamCtxt: malloc failed\n");
1761 return(-1);
1762 }
1763 comp->states = cur;
1764 comp->maxState *= 2;
1765 }
1766 comp->states[2 * comp->nbState] = idx;
1767 comp->states[2 * comp->nbState++ + 1] = level;
1768 return(comp->nbState - 1);
1769}
1770
1771/**
1772 * xmlStreamPushInternal:
1773 * @stream: the stream context
1774 * @name: the current name
1775 * @ns: the namespace name
1776 * @nodeType: the type of the node
1777 *
1778 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1779 * indicated a dictionary, then strings for name and ns will be expected
1780 * to come from the dictionary.
1781 * Both @name and @ns being NULL means the / i.e. the root of the document.
1782 * This can also act as a reset.
1783 *
1784 * Returns: -1 in case of error, 1 if the current state in the stream is a
1785 * match and 0 otherwise.
1786 */
1787static int
1788xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1789 const xmlChar *name, const xmlChar *ns,
1790 int nodeType) {
1791 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1792 xmlStreamCompPtr comp;
1793 xmlStreamStep step;
1794
1795 if ((stream == NULL) || (stream->nbState < 0))
1796 return(-1);
1797
1798 while (stream != NULL) {
1799 comp = stream->comp;
1800
1801 if ((nodeType == XML_ELEMENT_NODE) &&
1802 (name == NULL) && (ns == NULL)) {
1803 /* We have a document node here (or a reset). */
1804 stream->nbState = 0;
1805 stream->level = 0;
1806 stream->blockLevel = -1;
1807 if (comp->flags & XML_STREAM_FROM_ROOT) {
1808 if (comp->nbStep == 0) {
1809 /* TODO: We have a "/." here? */
1810 ret = 1;
1811 } else {
1812 if ((comp->nbStep == 1) &&
1813 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1814 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1815 {
1816 /*
1817 * In the case of "//." the document node will match
1818 * as well.
1819 */
1820 ret = 1;
1821 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1822 /* TODO: Do we need this ? */
1823 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1824 if (tmp < 0)
1825 err++;
1826 }
1827 }
1828 }
1829 stream = stream->next;
1830 continue; /* while */
1831 }
1832
1833 /*
1834 * Fast check for ".".
1835 */
1836 if (comp->nbStep == 0) {
1837 /*
1838 * / and . are handled at the XPath node set creation
1839 * level by checking min depth
1840 */
1841 if (stream->flags & XML_PATTERN_XPATH) {
1842 stream = stream->next;
1843 continue; /* while */
1844 }
1845 /*
1846 * For non-pattern like evaluation like XML Schema IDCs
1847 * or traditional XPath expressions, this will match if
1848 * we are at the first level only, otherwise on every level.
1849 */
1850 if ((nodeType != XML_ATTRIBUTE_NODE) &&
1851 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1852 (stream->level == 0))) {
1853 ret = 1;
1854 }
1855 stream->level++;
1856 goto stream_next;
1857 }
1858 if (stream->blockLevel != -1) {
1859 /*
1860 * Skip blocked expressions.
1861 */
1862 stream->level++;
1863 goto stream_next;
1864 }
1865
1866 if ((nodeType != XML_ELEMENT_NODE) &&
1867 (nodeType != XML_ATTRIBUTE_NODE) &&
1868 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1869 /*
1870 * No need to process nodes of other types if we don't
1871 * resolve to those types.
1872 * TODO: Do we need to block the context here?
1873 */
1874 stream->level++;
1875 goto stream_next;
1876 }
1877
1878 /*
1879 * Check evolution of existing states
1880 */
1881 i = 0;
1882 m = stream->nbState;
1883 while (i < m) {
1884 if ((comp->flags & XML_STREAM_DESC) == 0) {
1885 /*
1886 * If there is no "//", then only the last
1887 * added state is of interest.
1888 */
1889 stepNr = stream->states[2 * (stream->nbState -1)];
1890 /*
1891 * TODO: Security check, should not happen, remove it.
1892 */
1893 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1894 stream->level) {
1895 return (-1);
1896 }
1897 desc = 0;
1898 /* loop-stopper */
1899 i = m;
1900 } else {
1901 /*
1902 * If there are "//", then we need to process every "//"
1903 * occurring in the states, plus any other state for this
1904 * level.
1905 */
1906 stepNr = stream->states[2 * i];
1907
1908 /* TODO: should not happen anymore: dead states */
1909 if (stepNr < 0)
1910 goto next_state;
1911
1912 tmp = stream->states[(2 * i) + 1];
1913
1914 /* skip new states just added */
1915 if (tmp > stream->level)
1916 goto next_state;
1917
1918 /* skip states at ancestor levels, except if "//" */
1919 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1920 if ((tmp < stream->level) && (!desc))
1921 goto next_state;
1922 }
1923 /*
1924 * Check for correct node-type.
1925 */
1926 step = comp->steps[stepNr];
1927 if (step.nodeType != nodeType) {
1928 if (step.nodeType == XML_ATTRIBUTE_NODE) {
1929 /*
1930 * Block this expression for deeper evaluation.
1931 */
1932 if ((comp->flags & XML_STREAM_DESC) == 0)
1933 stream->blockLevel = stream->level +1;
1934 goto next_state;
1935 } else if (step.nodeType != XML_STREAM_ANY_NODE)
1936 goto next_state;
1937 }
1938 /*
1939 * Compare local/namespace-name.
1940 */
1941 match = 0;
1942 if (step.nodeType == XML_STREAM_ANY_NODE) {
1943 match = 1;
1944 } else if (step.name == NULL) {
1945 if (step.ns == NULL) {
1946 /*
1947 * This lets through all elements/attributes.
1948 */
1949 match = 1;
1950 } else if (ns != NULL)
1951 match = xmlStrEqual(step.ns, ns);
1952 } else if (((step.ns != NULL) == (ns != NULL)) &&
1953 (name != NULL) &&
1954 (step.name[0] == name[0]) &&
1955 xmlStrEqual(step.name, name) &&
1956 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
1957 {
1958 match = 1;
1959 }
1960#if 0
1961/*
1962* TODO: Pointer comparison won't work, since not guaranteed that the given
1963* values are in the same dict; especially if it's the namespace name,
1964* normally coming from ns->href. We need a namespace dict mechanism !
1965*/
1966 } else if (comp->dict) {
1967 if (step.name == NULL) {
1968 if (step.ns == NULL)
1969 match = 1;
1970 else
1971 match = (step.ns == ns);
1972 } else {
1973 match = ((step.name == name) && (step.ns == ns));
1974 }
1975#endif /* if 0 ------------------------------------------------------- */
1976 if (match) {
1977 final = step.flags & XML_STREAM_STEP_FINAL;
1978 if (final) {
1979 ret = 1;
1980 } else {
1981 xmlStreamCtxtAddState(stream, stepNr + 1,
1982 stream->level + 1);
1983 }
1984 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
1985 /*
1986 * Check if we have a special case like "foo/bar//.", where
1987 * "foo" is selected as well.
1988 */
1989 ret = 1;
1990 }
1991 }
1992 if (((comp->flags & XML_STREAM_DESC) == 0) &&
1993 ((! match) || final)) {
1994 /*
1995 * Mark this expression as blocked for any evaluation at
1996 * deeper levels. Note that this includes "/foo"
1997 * expressions if the *pattern* behaviour is used.
1998 */
1999 stream->blockLevel = stream->level +1;
2000 }
2001next_state:
2002 i++;
2003 }
2004
2005 stream->level++;
2006
2007 /*
2008 * Re/enter the expression.
2009 * Don't reenter if it's an absolute expression like "/foo",
2010 * except "//foo".
2011 */
2012 step = comp->steps[0];
2013 if (step.flags & XML_STREAM_STEP_ROOT)
2014 goto stream_next;
2015
2016 desc = step.flags & XML_STREAM_STEP_DESC;
2017 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2018 /*
2019 * Re/enter the expression if it is a "descendant" one,
2020 * or if we are at the 1st level of evaluation.
2021 */
2022
2023 if (stream->level == 1) {
2024 if (XML_STREAM_XS_IDC(stream)) {
2025 /*
2026 * XS-IDC: The missing "self::node()" will always
2027 * match the first given node.
2028 */
2029 goto stream_next;
2030 } else
2031 goto compare;
2032 }
2033 /*
2034 * A "//" is always reentrant.
2035 */
2036 if (desc)
2037 goto compare;
2038
2039 /*
2040 * XS-IDC: Process the 2nd level, since the missing
2041 * "self::node()" is responsible for the 2nd level being
2042 * the real start level.
2043 */
2044 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2045 goto compare;
2046
2047 goto stream_next;
2048 }
2049
2050compare:
2051 /*
2052 * Check expected node-type.
2053 */
2054 if (step.nodeType != nodeType) {
2055 if (nodeType == XML_ATTRIBUTE_NODE)
2056 goto stream_next;
2057 else if (step.nodeType != XML_STREAM_ANY_NODE)
2058 goto stream_next;
2059 }
2060 /*
2061 * Compare local/namespace-name.
2062 */
2063 match = 0;
2064 if (step.nodeType == XML_STREAM_ANY_NODE) {
2065 match = 1;
2066 } else if (step.name == NULL) {
2067 if (step.ns == NULL) {
2068 /*
2069 * This lets through all elements/attributes.
2070 */
2071 match = 1;
2072 } else if (ns != NULL)
2073 match = xmlStrEqual(step.ns, ns);
2074 } else if (((step.ns != NULL) == (ns != NULL)) &&
2075 (name != NULL) &&
2076 (step.name[0] == name[0]) &&
2077 xmlStrEqual(step.name, name) &&
2078 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2079 {
2080 match = 1;
2081 }
2082 final = step.flags & XML_STREAM_STEP_FINAL;
2083 if (match) {
2084 if (final)
2085 ret = 1;
2086 else
2087 xmlStreamCtxtAddState(stream, 1, stream->level);
2088 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2089 /*
2090 * Check if we have a special case like "foo//.", where
2091 * "foo" is selected as well.
2092 */
2093 ret = 1;
2094 }
2095 }
2096 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2097 ((! match) || final)) {
2098 /*
2099 * Mark this expression as blocked for any evaluation at
2100 * deeper levels.
2101 */
2102 stream->blockLevel = stream->level;
2103 }
2104
2105stream_next:
2106 stream = stream->next;
2107 } /* while stream != NULL */
2108
2109 if (err > 0)
2110 ret = -1;
2111 return(ret);
2112}
2113
2114/**
2115 * xmlStreamPush:
2116 * @stream: the stream context
2117 * @name: the current name
2118 * @ns: the namespace name
2119 *
2120 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2121 * indicated a dictionary, then strings for name and ns will be expected
2122 * to come from the dictionary.
2123 * Both @name and @ns being NULL means the / i.e. the root of the document.
2124 * This can also act as a reset.
2125 * Otherwise the function will act as if it has been given an element-node.
2126 *
2127 * Returns: -1 in case of error, 1 if the current state in the stream is a
2128 * match and 0 otherwise.
2129 */
2130int
2131xmlStreamPush(xmlStreamCtxtPtr stream,
2132 const xmlChar *name, const xmlChar *ns) {
2133 return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
2134}
2135
2136/**
2137 * xmlStreamPushNode:
2138 * @stream: the stream context
2139 * @name: the current name
2140 * @ns: the namespace name
2141 * @nodeType: the type of the node being pushed
2142 *
2143 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2144 * indicated a dictionary, then strings for name and ns will be expected
2145 * to come from the dictionary.
2146 * Both @name and @ns being NULL means the / i.e. the root of the document.
2147 * This can also act as a reset.
2148 * Different from xmlStreamPush() this function can be fed with nodes of type:
2149 * element-, attribute-, text-, cdata-section-, comment- and
2150 * processing-instruction-node.
2151 *
2152 * Returns: -1 in case of error, 1 if the current state in the stream is a
2153 * match and 0 otherwise.
2154 */
2155int
2156xmlStreamPushNode(xmlStreamCtxtPtr stream,
2157 const xmlChar *name, const xmlChar *ns,
2158 int nodeType)
2159{
2160 return (xmlStreamPushInternal(stream, name, ns,
2161 nodeType));
2162}
2163
2164/**
2165* xmlStreamPushAttr:
2166* @stream: the stream context
2167* @name: the current name
2168* @ns: the namespace name
2169*
2170* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2171* indicated a dictionary, then strings for name and ns will be expected
2172* to come from the dictionary.
2173* Both @name and @ns being NULL means the / i.e. the root of the document.
2174* This can also act as a reset.
2175* Otherwise the function will act as if it has been given an attribute-node.
2176*
2177* Returns: -1 in case of error, 1 if the current state in the stream is a
2178* match and 0 otherwise.
2179*/
2180int
2181xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2182 const xmlChar *name, const xmlChar *ns) {
2183 return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
2184}
2185
2186/**
2187 * xmlStreamPop:
2188 * @stream: the stream context
2189 *
2190 * push one level from the stream.
2191 *
2192 * Returns: -1 in case of error, 0 otherwise.
2193 */
2194int
2195xmlStreamPop(xmlStreamCtxtPtr stream) {
2196 int i, lev;
2197
2198 if (stream == NULL)
2199 return(-1);
2200 while (stream != NULL) {
2201 /*
2202 * Reset block-level.
2203 */
2204 if (stream->blockLevel == stream->level)
2205 stream->blockLevel = -1;
2206
2207 /*
2208 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2209 * (see the thread at
2210 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2211 */
2212 if (stream->level)
2213 stream->level--;
2214 /*
2215 * Check evolution of existing states
2216 */
2217 for (i = stream->nbState -1; i >= 0; i--) {
2218 /* discard obsoleted states */
2219 lev = stream->states[(2 * i) + 1];
2220 if (lev > stream->level)
2221 stream->nbState--;
2222 if (lev <= stream->level)
2223 break;
2224 }
2225 stream = stream->next;
2226 }
2227 return(0);
2228}
2229
2230/**
2231 * xmlStreamWantsAnyNode:
2232 * @streamCtxt: the stream context
2233 *
2234 * Query if the streaming pattern additionally needs to be fed with
2235 * text-, cdata-section-, comment- and processing-instruction-nodes.
2236 * If the result is 0 then only element-nodes and attribute-nodes
2237 * need to be pushed.
2238 *
2239 * Returns: 1 in case of need of nodes of the above described types,
2240 * 0 otherwise. -1 on API errors.
2241 */
2242int
2243xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2244{
2245 if (streamCtxt == NULL)
2246 return(-1);
2247 while (streamCtxt != NULL) {
2248 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2249 return(1);
2250 streamCtxt = streamCtxt->next;
2251 }
2252 return(0);
2253}
2254
2255/************************************************************************
2256 * *
2257 * The public interfaces *
2258 * *
2259 ************************************************************************/
2260
2261/**
2262 * xmlPatterncompile:
2263 * @pattern: the pattern to compile
2264 * @dict: an optional dictionary for interned strings
2265 * @flags: compilation flags, see xmlPatternFlags
2266 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2267 *
2268 * Compile a pattern.
2269 *
2270 * Returns the compiled form of the pattern or NULL in case of error
2271 */
2272xmlPatternPtr
2273xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2274 const xmlChar **namespaces) {
2275 xmlPatternPtr ret = NULL, cur;
2276 xmlPatParserContextPtr ctxt = NULL;
2277 const xmlChar *or, *start;
2278 xmlChar *tmp = NULL;
2279 int type = 0;
2280 int streamable = 1;
2281
2282 if (pattern == NULL)
2283 return(NULL);
2284
2285 start = pattern;
2286 or = start;
2287 while (*or != 0) {
2288 tmp = NULL;
2289 while ((*or != 0) && (*or != '|')) or++;
2290 if (*or == 0)
2291 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2292 else {
2293 tmp = xmlStrndup(start, or - start);
2294 if (tmp != NULL) {
2295 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2296 }
2297 or++;
2298 }
2299 if (ctxt == NULL) goto error;
2300 cur = xmlNewPattern();
2301 if (cur == NULL) goto error;
2302 /*
2303 * Assign string dict.
2304 */
2305 if (dict) {
2306 cur->dict = dict;
2307 xmlDictReference(dict);
2308 }
2309 if (ret == NULL)
2310 ret = cur;
2311 else {
2312 cur->next = ret->next;
2313 ret->next = cur;
2314 }
2315 cur->flags = flags;
2316 ctxt->comp = cur;
2317
2318 if (XML_STREAM_XS_IDC(cur))
2319 xmlCompileIDCXPathPath(ctxt);
2320 else
2321 xmlCompilePathPattern(ctxt);
2322 if (ctxt->error != 0)
2323 goto error;
2324 xmlFreePatParserContext(ctxt);
2325 ctxt = NULL;
2326
2327
2328 if (streamable) {
2329 if (type == 0) {
2330 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2331 } else if (type == PAT_FROM_ROOT) {
2332 if (cur->flags & PAT_FROM_CUR)
2333 streamable = 0;
2334 } else if (type == PAT_FROM_CUR) {
2335 if (cur->flags & PAT_FROM_ROOT)
2336 streamable = 0;
2337 }
2338 }
2339 if (streamable)
2340 xmlStreamCompile(cur);
2341 if (xmlReversePattern(cur) < 0)
2342 goto error;
2343 if (tmp != NULL) {
2344 xmlFree(tmp);
2345 tmp = NULL;
2346 }
2347 start = or;
2348 }
2349 if (streamable == 0) {
2350 cur = ret;
2351 while (cur != NULL) {
2352 if (cur->stream != NULL) {
2353 xmlFreeStreamComp(cur->stream);
2354 cur->stream = NULL;
2355 }
2356 cur = cur->next;
2357 }
2358 }
2359
2360 return(ret);
2361error:
2362 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2363 if (ret != NULL) xmlFreePattern(ret);
2364 if (tmp != NULL) xmlFree(tmp);
2365 return(NULL);
2366}
2367
2368/**
2369 * xmlPatternMatch:
2370 * @comp: the precompiled pattern
2371 * @node: a node
2372 *
2373 * Test whether the node matches the pattern
2374 *
2375 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2376 */
2377int
2378xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2379{
2380 int ret = 0;
2381
2382 if ((comp == NULL) || (node == NULL))
2383 return(-1);
2384
2385 while (comp != NULL) {
2386 ret = xmlPatMatch(comp, node);
2387 if (ret != 0)
2388 return(ret);
2389 comp = comp->next;
2390 }
2391 return(ret);
2392}
2393
2394/**
2395 * xmlPatternGetStreamCtxt:
2396 * @comp: the precompiled pattern
2397 *
2398 * Get a streaming context for that pattern
2399 * Use xmlFreeStreamCtxt to free the context.
2400 *
2401 * Returns a pointer to the context or NULL in case of failure
2402 */
2403xmlStreamCtxtPtr
2404xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2405{
2406 xmlStreamCtxtPtr ret = NULL, cur;
2407
2408 if ((comp == NULL) || (comp->stream == NULL))
2409 return(NULL);
2410
2411 while (comp != NULL) {
2412 if (comp->stream == NULL)
2413 goto failed;
2414 cur = xmlNewStreamCtxt(comp->stream);
2415 if (cur == NULL)
2416 goto failed;
2417 if (ret == NULL)
2418 ret = cur;
2419 else {
2420 cur->next = ret->next;
2421 ret->next = cur;
2422 }
2423 cur->flags = comp->flags;
2424 comp = comp->next;
2425 }
2426 return(ret);
2427failed:
2428 xmlFreeStreamCtxt(ret);
2429 return(NULL);
2430}
2431
2432/**
2433 * xmlPatternStreamable:
2434 * @comp: the precompiled pattern
2435 *
2436 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2437 * should work.
2438 *
2439 * Returns 1 if streamable, 0 if not and -1 in case of error.
2440 */
2441int
2442xmlPatternStreamable(xmlPatternPtr comp) {
2443 if (comp == NULL)
2444 return(-1);
2445 while (comp != NULL) {
2446 if (comp->stream == NULL)
2447 return(0);
2448 comp = comp->next;
2449 }
2450 return(1);
2451}
2452
2453/**
2454 * xmlPatternMaxDepth:
2455 * @comp: the precompiled pattern
2456 *
2457 * Check the maximum depth reachable by a pattern
2458 *
2459 * Returns -2 if no limit (using //), otherwise the depth,
2460 * and -1 in case of error
2461 */
2462int
2463xmlPatternMaxDepth(xmlPatternPtr comp) {
2464 int ret = 0, i;
2465 if (comp == NULL)
2466 return(-1);
2467 while (comp != NULL) {
2468 if (comp->stream == NULL)
2469 return(-1);
2470 for (i = 0;i < comp->stream->nbStep;i++)
2471 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2472 return(-2);
2473 if (comp->stream->nbStep > ret)
2474 ret = comp->stream->nbStep;
2475 comp = comp->next;
2476 }
2477 return(ret);
2478}
2479
2480/**
2481 * xmlPatternMinDepth:
2482 * @comp: the precompiled pattern
2483 *
2484 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2485 * part of the set.
2486 *
2487 * Returns -1 in case of error otherwise the depth,
2488 *
2489 */
2490int
2491xmlPatternMinDepth(xmlPatternPtr comp) {
2492 int ret = 12345678;
2493 if (comp == NULL)
2494 return(-1);
2495 while (comp != NULL) {
2496 if (comp->stream == NULL)
2497 return(-1);
2498 if (comp->stream->nbStep < ret)
2499 ret = comp->stream->nbStep;
2500 if (ret == 0)
2501 return(0);
2502 comp = comp->next;
2503 }
2504 return(ret);
2505}
2506
2507/**
2508 * xmlPatternFromRoot:
2509 * @comp: the precompiled pattern
2510 *
2511 * Check if the pattern must be looked at from the root.
2512 *
2513 * Returns 1 if true, 0 if false and -1 in case of error
2514 */
2515int
2516xmlPatternFromRoot(xmlPatternPtr comp) {
2517 if (comp == NULL)
2518 return(-1);
2519 while (comp != NULL) {
2520 if (comp->stream == NULL)
2521 return(-1);
2522 if (comp->flags & PAT_FROM_ROOT)
2523 return(1);
2524 comp = comp->next;
2525 }
2526 return(0);
2527
2528}
2529
2530#endif /* LIBXML_PATTERN_ENABLED */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use