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