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