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