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