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