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