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