blob: ab1831d7f24acf21fc79a1cbdc6d7f3019ec648a [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6 * to some extent
7 * http://www.w3.org/TR/1999/REC-xml-19991116
8 *
9 * See Copyright for the status of this software.
10 *
11 * daniel@veillard.com
12 */
13
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
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 * - xmlPatterncompile support of namespaces arguments, I'm not sure
23 * it's implemented and definitely not tested
24 * - handling of disjunction "pattern1 | pattern2" mean needed to build
25 * and check a list internally
26 */
27
Daniel Veillardb3de70c2003-12-02 22:32:15 +000028#define IN_LIBXML
29#include "libxml.h"
30
31#include <string.h>
32#include <libxml/xmlmemory.h>
33#include <libxml/tree.h>
34#include <libxml/hash.h>
35#include <libxml/dict.h>
36#include <libxml/xmlerror.h>
37#include <libxml/parserInternals.h>
38#include <libxml/pattern.h>
39
Daniel Veillard364fbed2005-02-03 09:08:06 +000040/* #ifdef LIBXML_PATTERN_ENABLED */
Daniel Veillardb3de70c2003-12-02 22:32:15 +000041
Daniel Veillard9740d1d2005-02-01 16:21:43 +000042#define DEBUG_STREAMING
Daniel Veillard2fc6df92005-01-30 18:42:55 +000043
Daniel Veillardb3de70c2003-12-02 22:32:15 +000044#define ERROR(a, b, c, d)
45#define ERROR5(a, b, c, d, e)
46
Daniel Veillard2fc6df92005-01-30 18:42:55 +000047#define XML_STREAM_STEP_DESC 1
48#define XML_STREAM_STEP_FINAL 2
49#define XML_STREAM_STEP_ROOT 4
50
51typedef struct _xmlStreamStep xmlStreamStep;
52typedef xmlStreamStep *xmlStreamStepPtr;
53struct _xmlStreamStep {
54 int flags; /* properties of that step */
55 const xmlChar *name; /* first string value if NULL accept all */
56 const xmlChar *ns; /* second string value */
57};
58
59typedef struct _xmlStreamComp xmlStreamComp;
60typedef xmlStreamComp *xmlStreamCompPtr;
61struct _xmlStreamComp {
62 xmlDict *dict; /* the dictionnary if any */
63 int nbStep; /* number of steps in the automata */
64 int maxStep; /* allocated number of steps */
65 xmlStreamStepPtr steps; /* the array of steps */
66};
67
68struct _xmlStreamCtxt {
69 xmlStreamCompPtr comp; /* the compiled stream */
70 int nbState; /* number of state in the automata */
71 int maxState; /* allocated number of state */
72 int level; /* how deep are we ? */
73 int *states; /* the array of step indexes */
74};
75
76static void xmlFreeStreamComp(xmlStreamCompPtr comp);
77
Daniel Veillardb3de70c2003-12-02 22:32:15 +000078/*
79 * Types are private:
80 */
81
82typedef enum {
83 XML_OP_END=0,
84 XML_OP_ROOT,
85 XML_OP_ELEM,
86 XML_OP_CHILD,
87 XML_OP_ATTR,
88 XML_OP_PARENT,
89 XML_OP_ANCESTOR,
90 XML_OP_NS,
91 XML_OP_ALL
92} xmlPatOp;
93
94
95typedef struct _xmlStepOp xmlStepOp;
96typedef xmlStepOp *xmlStepOpPtr;
97struct _xmlStepOp {
98 xmlPatOp op;
99 const xmlChar *value;
100 const xmlChar *value2;
101};
102
103struct _xmlPattern {
104 void *data; /* the associated template */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000105 xmlDictPtr dict; /* the optional dictionnary */
106 struct _xmlPattern *next; /* siblings */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000107 const xmlChar *pattern; /* the pattern */
108
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000109 int nbStep;
110 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000111 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000112 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000113};
114
115typedef struct _xmlPatParserContext xmlPatParserContext;
116typedef xmlPatParserContext *xmlPatParserContextPtr;
117struct _xmlPatParserContext {
118 const xmlChar *cur; /* the current char being parsed */
119 const xmlChar *base; /* the full expression */
120 int error; /* error code */
121 xmlDictPtr dict; /* the dictionnary if any */
122 xmlPatternPtr comp; /* the result */
123 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000124 const xmlChar **namespaces; /* the namespaces definitions */
125 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000126};
127
128/************************************************************************
129 * *
130 * Type functions *
131 * *
132 ************************************************************************/
133
134/**
135 * xmlNewPattern:
136 *
137 * Create a new XSLT Pattern
138 *
139 * Returns the newly allocated xmlPatternPtr or NULL in case of error
140 */
141static xmlPatternPtr
142xmlNewPattern(void) {
143 xmlPatternPtr cur;
144
145 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
146 if (cur == NULL) {
147 ERROR(NULL, NULL, NULL,
148 "xmlNewPattern : malloc failed\n");
149 return(NULL);
150 }
151 memset(cur, 0, sizeof(xmlPattern));
152 cur->maxStep = 10;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000153 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
154 if (cur->steps == NULL) {
155 xmlFree(cur);
156 ERROR(NULL, NULL, NULL,
157 "xmlNewPattern : malloc failed\n");
158 return(NULL);
159 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000160 return(cur);
161}
162
163/**
164 * xmlFreePattern:
165 * @comp: an XSLT comp
166 *
167 * Free up the memory allocated by @comp
168 */
169void
170xmlFreePattern(xmlPatternPtr comp) {
171 xmlStepOpPtr op;
172 int i;
173
174 if (comp == NULL)
175 return;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000176 if (comp->stream != NULL)
177 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000178 if (comp->pattern != NULL)
179 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000180 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000181 if (comp->dict == NULL) {
182 for (i = 0;i < comp->nbStep;i++) {
183 op = &comp->steps[i];
184 if (op->value != NULL)
185 xmlFree((xmlChar *) op->value);
186 if (op->value2 != NULL)
187 xmlFree((xmlChar *) op->value2);
188 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000189 }
190 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000191 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000192 if (comp->dict != NULL)
193 xmlDictFree(comp->dict);
194
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000195 memset(comp, -1, sizeof(xmlPattern));
196 xmlFree(comp);
197}
198
199/**
200 * xmlFreePatternList:
201 * @comp: an XSLT comp list
202 *
203 * Free up the memory allocated by all the elements of @comp
204 */
205void
206xmlFreePatternList(xmlPatternPtr comp) {
207 xmlPatternPtr cur;
208
209 while (comp != NULL) {
210 cur = comp;
211 comp = comp->next;
212 xmlFreePattern(cur);
213 }
214}
215
216/**
217 * xmlNewPatParserContext:
218 * @pattern: the pattern context
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000219 * @dict: the inherited dictionnary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000220 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
221 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000222 *
223 * Create a new XML pattern parser context
224 *
225 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
226 */
227static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000228xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
229 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000230 xmlPatParserContextPtr cur;
231
232 if (pattern == NULL)
233 return(NULL);
234
235 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
236 if (cur == NULL) {
237 ERROR(NULL, NULL, NULL,
238 "xmlNewPatParserContext : malloc failed\n");
239 return(NULL);
240 }
241 memset(cur, 0, sizeof(xmlPatParserContext));
242 cur->dict = dict;
243 cur->cur = pattern;
244 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000245 if (namespaces != NULL) {
246 int i;
247 for (i = 0;namespaces[2 * i] != NULL;i++);
248 cur->nb_namespaces = i;
249 } else {
250 cur->nb_namespaces = 0;
251 }
252 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000253 return(cur);
254}
255
256/**
257 * xmlFreePatParserContext:
258 * @ctxt: an XSLT parser context
259 *
260 * Free up the memory allocated by @ctxt
261 */
262static void
263xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
264 if (ctxt == NULL)
265 return;
266 memset(ctxt, -1, sizeof(xmlPatParserContext));
267 xmlFree(ctxt);
268}
269
270/**
271 * xmlPatternAdd:
272 * @comp: the compiled match expression
273 * @op: an op
274 * @value: the first value
275 * @value2: the second value
276 *
277 * Add an step to an XSLT Compiled Match
278 *
279 * Returns -1 in case of failure, 0 otherwise.
280 */
281static int
282xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
283 xmlPatternPtr comp,
284 xmlPatOp op, xmlChar * value, xmlChar * value2)
285{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000286 if (comp->nbStep >= comp->maxStep) {
287 xmlStepOpPtr temp;
288 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
289 sizeof(xmlStepOp));
290 if (temp == NULL) {
291 ERROR(ctxt, NULL, NULL,
292 "xmlPatternAdd: realloc failed\n");
293 return (-1);
294 }
295 comp->steps = temp;
296 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000297 }
298 comp->steps[comp->nbStep].op = op;
299 comp->steps[comp->nbStep].value = value;
300 comp->steps[comp->nbStep].value2 = value2;
301 comp->nbStep++;
302 return (0);
303}
304
305#if 0
306/**
307 * xsltSwapTopPattern:
308 * @comp: the compiled match expression
309 *
310 * reverse the two top steps.
311 */
312static void
313xsltSwapTopPattern(xmlPatternPtr comp) {
314 int i;
315 int j = comp->nbStep - 1;
316
317 if (j > 0) {
318 register const xmlChar *tmp;
319 register xmlPatOp op;
320 i = j - 1;
321 tmp = comp->steps[i].value;
322 comp->steps[i].value = comp->steps[j].value;
323 comp->steps[j].value = tmp;
324 tmp = comp->steps[i].value2;
325 comp->steps[i].value2 = comp->steps[j].value2;
326 comp->steps[j].value2 = tmp;
327 op = comp->steps[i].op;
328 comp->steps[i].op = comp->steps[j].op;
329 comp->steps[j].op = op;
330 }
331}
332#endif
333
334/**
335 * xmlReversePattern:
336 * @comp: the compiled match expression
337 *
338 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000339 *
340 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000341 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000342static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000343xmlReversePattern(xmlPatternPtr comp) {
344 int i = 0;
345 int j = comp->nbStep - 1;
346
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000347 if (comp->nbStep >= comp->maxStep) {
348 xmlStepOpPtr temp;
349 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
350 sizeof(xmlStepOp));
351 if (temp == NULL) {
352 ERROR(ctxt, NULL, NULL,
353 "xmlReversePattern: realloc failed\n");
354 return (-1);
355 }
356 comp->steps = temp;
357 comp->maxStep *= 2;
358 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000359 while (j > i) {
360 register const xmlChar *tmp;
361 register xmlPatOp op;
362 tmp = comp->steps[i].value;
363 comp->steps[i].value = comp->steps[j].value;
364 comp->steps[j].value = tmp;
365 tmp = comp->steps[i].value2;
366 comp->steps[i].value2 = comp->steps[j].value2;
367 comp->steps[j].value2 = tmp;
368 op = comp->steps[i].op;
369 comp->steps[i].op = comp->steps[j].op;
370 comp->steps[j].op = op;
371 j--;
372 i++;
373 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000374 comp->steps[comp->nbStep].value = NULL;
375 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000376 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000377 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000378}
379
380/************************************************************************
381 * *
382 * The interpreter for the precompiled patterns *
383 * *
384 ************************************************************************/
385
386/**
387 * xmlPatMatch:
388 * @comp: the precompiled pattern
389 * @node: a node
390 *
391 * Test wether the node matches the pattern
392 *
393 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
394 */
395static int
396xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
397 int i;
398 xmlStepOpPtr step;
399
400 if ((comp == NULL) || (node == NULL)) return(-1);
401 for (i = 0;i < comp->nbStep;i++) {
402 step = &comp->steps[i];
403 switch (step->op) {
404 case XML_OP_END:
405 return(1);
406 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000407 if (node->type == XML_NAMESPACE_DECL)
408 return(0);
409 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000410 if ((node->type == XML_DOCUMENT_NODE) ||
411#ifdef LIBXML_DOCB_ENABLED
412 (node->type == XML_DOCB_DOCUMENT_NODE) ||
413#endif
414 (node->type == XML_HTML_DOCUMENT_NODE))
415 continue;
416 return(0);
417 case XML_OP_ELEM:
418 if (node->type != XML_ELEMENT_NODE)
419 return(0);
420 if (step->value == NULL)
421 continue;
422 if (step->value[0] != node->name[0])
423 return(0);
424 if (!xmlStrEqual(step->value, node->name))
425 return(0);
426
427 /* Namespace test */
428 if (node->ns == NULL) {
429 if (step->value2 != NULL)
430 return(0);
431 } else if (node->ns->href != NULL) {
432 if (step->value2 == NULL)
433 return(0);
434 if (!xmlStrEqual(step->value2, node->ns->href))
435 return(0);
436 }
437 continue;
438 case XML_OP_CHILD: {
439 xmlNodePtr lst;
440
441 if ((node->type != XML_ELEMENT_NODE) &&
442 (node->type != XML_DOCUMENT_NODE) &&
443#ifdef LIBXML_DOCB_ENABLED
444 (node->type != XML_DOCB_DOCUMENT_NODE) &&
445#endif
446 (node->type != XML_HTML_DOCUMENT_NODE))
447 return(0);
448
449 lst = node->children;
450
451 if (step->value != NULL) {
452 while (lst != NULL) {
453 if ((lst->type == XML_ELEMENT_NODE) &&
454 (step->value[0] == lst->name[0]) &&
455 (xmlStrEqual(step->value, lst->name)))
456 break;
457 lst = lst->next;
458 }
459 if (lst != NULL)
460 continue;
461 }
462 return(0);
463 }
464 case XML_OP_ATTR:
465 if (node->type != XML_ATTRIBUTE_NODE)
466 return(0);
467 if (step->value != NULL) {
468 if (step->value[0] != node->name[0])
469 return(0);
470 if (!xmlStrEqual(step->value, node->name))
471 return(0);
472 }
473 /* Namespace test */
474 if (node->ns == NULL) {
475 if (step->value2 != NULL)
476 return(0);
477 } else if (step->value2 != NULL) {
478 if (!xmlStrEqual(step->value2, node->ns->href))
479 return(0);
480 }
481 continue;
482 case XML_OP_PARENT:
483 if ((node->type == XML_DOCUMENT_NODE) ||
484 (node->type == XML_HTML_DOCUMENT_NODE) ||
485#ifdef LIBXML_DOCB_ENABLED
486 (node->type == XML_DOCB_DOCUMENT_NODE) ||
487#endif
488 (node->type == XML_NAMESPACE_DECL))
489 return(0);
490 node = node->parent;
491 if (node == NULL)
492 return(0);
493 if (step->value == NULL)
494 continue;
495 if (step->value[0] != node->name[0])
496 return(0);
497 if (!xmlStrEqual(step->value, node->name))
498 return(0);
499 /* Namespace test */
500 if (node->ns == NULL) {
501 if (step->value2 != NULL)
502 return(0);
503 } else if (node->ns->href != NULL) {
504 if (step->value2 == NULL)
505 return(0);
506 if (!xmlStrEqual(step->value2, node->ns->href))
507 return(0);
508 }
509 continue;
510 case XML_OP_ANCESTOR:
511 /* TODO: implement coalescing of ANCESTOR/NODE ops */
512 if (step->value == NULL) {
513 i++;
514 step = &comp->steps[i];
515 if (step->op == XML_OP_ROOT)
516 return(1);
517 if (step->op != XML_OP_ELEM)
518 return(0);
519 if (step->value == NULL)
520 return(-1);
521 }
522 if (node == NULL)
523 return(0);
524 if ((node->type == XML_DOCUMENT_NODE) ||
525 (node->type == XML_HTML_DOCUMENT_NODE) ||
526#ifdef LIBXML_DOCB_ENABLED
527 (node->type == XML_DOCB_DOCUMENT_NODE) ||
528#endif
529 (node->type == XML_NAMESPACE_DECL))
530 return(0);
531 node = node->parent;
532 while (node != NULL) {
533 if (node == NULL)
534 return(0);
535 if ((node->type == XML_ELEMENT_NODE) &&
536 (step->value[0] == node->name[0]) &&
537 (xmlStrEqual(step->value, node->name))) {
538 /* Namespace test */
539 if (node->ns == NULL) {
540 if (step->value2 == NULL)
541 break;
542 } else if (node->ns->href != NULL) {
543 if ((step->value2 != NULL) &&
544 (xmlStrEqual(step->value2, node->ns->href)))
545 break;
546 }
547 }
548 node = node->parent;
549 }
550 if (node == NULL)
551 return(0);
552 continue;
553 case XML_OP_NS:
554 if (node->type != XML_ELEMENT_NODE)
555 return(0);
556 if (node->ns == NULL) {
557 if (step->value != NULL)
558 return(0);
559 } else if (node->ns->href != NULL) {
560 if (step->value == NULL)
561 return(0);
562 if (!xmlStrEqual(step->value, node->ns->href))
563 return(0);
564 }
565 break;
566 case XML_OP_ALL:
567 if (node->type != XML_ELEMENT_NODE)
568 return(0);
569 break;
570 }
571 }
572 return(1);
573}
574
575/************************************************************************
576 * *
577 * Dedicated parser for templates *
578 * *
579 ************************************************************************/
580
581#define TODO \
582 xmlGenericError(xmlGenericErrorContext, \
583 "Unimplemented block at %s:%d\n", \
584 __FILE__, __LINE__);
585#define CUR (*ctxt->cur)
586#define SKIP(val) ctxt->cur += (val)
587#define NXT(val) ctxt->cur[(val)]
588#define CUR_PTR ctxt->cur
589
590#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000591 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000592
593#define CURRENT (*ctxt->cur)
594#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
595
596
597#define PUSH(op, val, val2) \
598 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
599
600#define XSLT_ERROR(X) \
601 { xsltError(ctxt, __FILE__, __LINE__, X); \
602 ctxt->error = (X); return; }
603
604#define XSLT_ERROR0(X) \
605 { xsltError(ctxt, __FILE__, __LINE__, X); \
606 ctxt->error = (X); return(0); }
607
608#if 0
609/**
610 * xmlPatScanLiteral:
611 * @ctxt: the XPath Parser context
612 *
613 * Parse an XPath Litteral:
614 *
615 * [29] Literal ::= '"' [^"]* '"'
616 * | "'" [^']* "'"
617 *
618 * Returns the Literal parsed or NULL
619 */
620
621static xmlChar *
622xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
623 const xmlChar *q, *cur;
624 xmlChar *ret = NULL;
625 int val, len;
626
627 SKIP_BLANKS;
628 if (CUR == '"') {
629 NEXT;
630 cur = q = CUR_PTR;
631 val = xmlStringCurrentChar(NULL, cur, &len);
632 while ((IS_CHAR(val)) && (val != '"')) {
633 cur += len;
634 val = xmlStringCurrentChar(NULL, cur, &len);
635 }
636 if (!IS_CHAR(val)) {
637 ctxt->error = 1;
638 return(NULL);
639 } else {
640 ret = xmlStrndup(q, cur - q);
641 }
642 cur += len;
643 CUR_PTR = cur;
644 } else if (CUR == '\'') {
645 NEXT;
646 cur = q = CUR_PTR;
647 val = xmlStringCurrentChar(NULL, cur, &len);
648 while ((IS_CHAR(val)) && (val != '\'')) {
649 cur += len;
650 val = xmlStringCurrentChar(NULL, cur, &len);
651 }
652 if (!IS_CHAR(val)) {
653 ctxt->error = 1;
654 return(NULL);
655 } else {
656 ret = xmlStrndup(q, cur - q);
657 }
658 cur += len;
659 CUR_PTR = cur;
660 } else {
661 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
662 ctxt->error = 1;
663 return(NULL);
664 }
665 return(ret);
666}
667#endif
668
669/**
670 * xmlPatScanName:
671 * @ctxt: the XPath Parser context
672 *
673 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
674 * CombiningChar | Extender
675 *
676 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
677 *
678 * [6] Names ::= Name (S Name)*
679 *
680 * Returns the Name parsed or NULL
681 */
682
683static xmlChar *
684xmlPatScanName(xmlPatParserContextPtr ctxt) {
685 const xmlChar *q, *cur;
686 xmlChar *ret = NULL;
687 int val, len;
688
689 SKIP_BLANKS;
690
691 cur = q = CUR_PTR;
692 val = xmlStringCurrentChar(NULL, cur, &len);
693 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
694 return(NULL);
695
696 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
697 (val == '.') || (val == '-') ||
698 (val == '_') ||
699 (IS_COMBINING(val)) ||
700 (IS_EXTENDER(val))) {
701 cur += len;
702 val = xmlStringCurrentChar(NULL, cur, &len);
703 }
704 ret = xmlStrndup(q, cur - q);
705 CUR_PTR = cur;
706 return(ret);
707}
708
709/**
710 * xmlPatScanNCName:
711 * @ctxt: the XPath Parser context
712 *
713 * Parses a non qualified name
714 *
715 * Returns the Name parsed or NULL
716 */
717
718static xmlChar *
719xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
720 const xmlChar *q, *cur;
721 xmlChar *ret = NULL;
722 int val, len;
723
724 SKIP_BLANKS;
725
726 cur = q = CUR_PTR;
727 val = xmlStringCurrentChar(NULL, cur, &len);
728 if (!IS_LETTER(val) && (val != '_'))
729 return(NULL);
730
731 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
732 (val == '.') || (val == '-') ||
733 (val == '_') ||
734 (IS_COMBINING(val)) ||
735 (IS_EXTENDER(val))) {
736 cur += len;
737 val = xmlStringCurrentChar(NULL, cur, &len);
738 }
739 ret = xmlStrndup(q, cur - q);
740 CUR_PTR = cur;
741 return(ret);
742}
743
744#if 0
745/**
746 * xmlPatScanQName:
747 * @ctxt: the XPath Parser context
748 * @prefix: the place to store the prefix
749 *
750 * Parse a qualified name
751 *
752 * Returns the Name parsed or NULL
753 */
754
755static xmlChar *
756xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
757 xmlChar *ret = NULL;
758
759 *prefix = NULL;
760 ret = xmlPatScanNCName(ctxt);
761 if (CUR == ':') {
762 *prefix = ret;
763 NEXT;
764 ret = xmlPatScanNCName(ctxt);
765 }
766 return(ret);
767}
768#endif
769
770/**
771 * xmlCompileStepPattern:
772 * @ctxt: the compilation context
773 *
774 * Compile the Step Pattern and generates a precompiled
775 * form suitable for fast matching.
776 *
777 * [3] Step ::= '.' | NameTest
778 * [4] NameTest ::= QName | '*' | NCName ':' '*'
779 */
780
781static void
782xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
783 xmlChar *token = NULL;
784 xmlChar *name = NULL;
785 const xmlChar *URI = NULL;
786 xmlChar *URL = NULL;
787
788 SKIP_BLANKS;
789 if (CUR == '.') {
790 NEXT;
791 PUSH(XML_OP_ELEM, NULL, NULL);
792 return;
793 }
794 name = xmlPatScanNCName(ctxt);
795 if (name == NULL) {
796 if (CUR == '*') {
797 NEXT;
798 PUSH(XML_OP_ALL, NULL, NULL);
799 return;
800 } else {
801 ERROR(NULL, NULL, NULL,
802 "xmlCompileStepPattern : Name expected\n");
803 ctxt->error = 1;
804 return;
805 }
806 }
807 SKIP_BLANKS;
808 if (CUR == ':') {
809 NEXT;
810 if (CUR != ':') {
811 xmlChar *prefix = name;
812 xmlNsPtr ns;
813
814 /*
815 * This is a namespace match
816 */
817 token = xmlPatScanName(ctxt);
818 ns = xmlSearchNs(NULL, ctxt->elem, prefix);
819 if (ns == NULL) {
820 ERROR5(NULL, NULL, NULL,
821 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
822 prefix);
823 ctxt->error = 1;
824 goto error;
825 } else {
826 URL = xmlStrdup(ns->href);
827 }
828 xmlFree(prefix);
829 if (token == NULL) {
830 if (CUR == '*') {
831 NEXT;
832 PUSH(XML_OP_NS, URL, NULL);
833 } else {
834 ERROR(NULL, NULL, NULL,
835 "xmlCompileStepPattern : Name expected\n");
836 ctxt->error = 1;
837 goto error;
838 }
839 } else {
840 PUSH(XML_OP_ELEM, token, URL);
841 }
842 } else {
843 NEXT;
844 if (xmlStrEqual(token, (const xmlChar *) "child")) {
845 xmlFree(token);
846 token = xmlPatScanName(ctxt);
847 if (token == NULL) {
848 if (CUR == '*') {
849 NEXT;
850 PUSH(XML_OP_ALL, token, NULL);
851 return;
852 } else {
853 ERROR(NULL, NULL, NULL,
854 "xmlCompileStepPattern : QName expected\n");
855 ctxt->error = 1;
856 goto error;
857 }
858 }
859 TODO
860 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
861 if (token == NULL) {
862 ctxt->error = 1;
863 goto error;
864 } else {
865 name = xmlStrdup(token);
866 if (URI != NULL)
867 URL = xmlStrdup(URI);
868 }
869 PUSH(XML_OP_CHILD, name, URL);
870 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
871 xmlFree(token);
872 token = xmlPatScanName(ctxt);
873 if (token == NULL) {
874 ERROR(NULL, NULL, NULL,
875 "xmlCompileStepPattern : QName expected\n");
876 ctxt->error = 1;
877 goto error;
878 }
879 TODO
880 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
881 if (token == NULL) {
882 ctxt->error = 1;
883 goto error;
884 } else {
885 name = xmlStrdup(token);
886 if (URI != NULL)
887 URL = xmlStrdup(URI);
888 }
889 PUSH(XML_OP_ATTR, name, URL);
890 } else {
891 ERROR(NULL, NULL, NULL,
892 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
893 ctxt->error = 1;
894 goto error;
895 }
896 xmlFree(token);
897 }
898 } else if (CUR == '*') {
899 NEXT;
900 PUSH(XML_OP_ALL, token, NULL);
901 } else {
902 if (name == NULL) {
903 ctxt->error = 1;
904 goto error;
905 }
906 PUSH(XML_OP_ELEM, name, NULL);
907 }
908 return;
909error:
910 if (token != NULL)
911 xmlFree(token);
912 if (name != NULL)
913 xmlFree(name);
914}
915
916/**
917 * xmlCompilePathPattern:
918 * @ctxt: the compilation context
919 *
920 * Compile the Path Pattern and generates a precompiled
921 * form suitable for fast matching.
922 *
923 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
924 */
925static void
926xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
927 SKIP_BLANKS;
928 if ((CUR == '/') && (NXT(1) == '/')) {
929 /*
930 * since we reverse the query
931 * a leading // can be safely ignored
932 */
933 NEXT;
934 NEXT;
935 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
936 /*
937 * a leading .// can be safely ignored
938 */
939 NEXT;
940 NEXT;
941 NEXT;
942 }
943 if (CUR == '@') {
944 TODO
945 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000946 if (CUR == '/') {
947 PUSH(XML_OP_ROOT, NULL, NULL);
948 NEXT;
949 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000950 xmlCompileStepPattern(ctxt);
951 SKIP_BLANKS;
952 while (CUR == '/') {
953 if ((CUR == '/') && (NXT(1) == '/')) {
954 PUSH(XML_OP_ANCESTOR, NULL, NULL);
955 NEXT;
956 NEXT;
957 SKIP_BLANKS;
958 xmlCompileStepPattern(ctxt);
959 } else {
960 PUSH(XML_OP_PARENT, NULL, NULL);
961 NEXT;
962 SKIP_BLANKS;
963 if ((CUR != 0) || (CUR == '|')) {
964 xmlCompileStepPattern(ctxt);
965 }
966 }
967 }
968 }
969error:
970 return;
971}
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000972
973/************************************************************************
974 * *
975 * The streaming code *
976 * *
977 ************************************************************************/
978
979#ifdef DEBUG_STREAMING
980static void
981xmlDebugStreamComp(xmlStreamCompPtr stream) {
982 int i;
983
984 if (stream == NULL) {
985 printf("Stream: NULL\n");
986 return;
987 }
988 printf("Stream: %d steps\n", stream->nbStep);
989 for (i = 0;i < stream->nbStep;i++) {
990 if (stream->steps[i].ns != NULL) {
991 printf("{%s}", stream->steps[i].ns);
992 }
993 if (stream->steps[i].name == NULL) {
994 printf("* ");
995 } else {
996 printf("%s ", stream->steps[i].name);
997 }
998 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
999 printf("root ");
1000 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1001 printf("// ");
1002 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1003 printf("final ");
1004 printf("\n");
1005 }
1006}
1007static void
1008xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1009 int i;
1010
1011 if (ctxt == NULL) {
1012 printf("Stream: NULL\n");
1013 return;
1014 }
1015 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1016 if (match)
1017 printf("matches\n");
1018 else
1019 printf("\n");
1020 for (i = 0;i < ctxt->nbState;i++) {
1021 if (ctxt->states[2 * i] < 0)
1022 printf(" %d: free\n", i);
1023 else {
1024 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1025 ctxt->states[(2 * i) + 1]);
1026 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1027 XML_STREAM_STEP_DESC)
1028 printf(" //\n");
1029 else
1030 printf("\n");
1031 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001032 }
1033}
1034#endif
1035/**
1036 * xmlNewStreamComp:
1037 * @size: the number of expected steps
1038 *
1039 * build a new compiled pattern for streaming
1040 *
1041 * Returns the new structure or NULL in case of error.
1042 */
1043static xmlStreamCompPtr
1044xmlNewStreamComp(int size) {
1045 xmlStreamCompPtr cur;
1046
1047 if (size < 4)
1048 size = 4;
1049
1050 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1051 if (cur == NULL) {
1052 ERROR(NULL, NULL, NULL,
1053 "xmlNewStreamComp: malloc failed\n");
1054 return(NULL);
1055 }
1056 memset(cur, 0, sizeof(xmlStreamComp));
1057 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1058 if (cur->steps == NULL) {
1059 xmlFree(cur);
1060 ERROR(NULL, NULL, NULL,
1061 "xmlNewStreamComp: malloc failed\n");
1062 return(NULL);
1063 }
1064 cur->nbStep = 0;
1065 cur->maxStep = size;
1066 return(cur);
1067}
1068
1069/**
1070 * xmlFreeStreamComp:
1071 * @comp: the compiled pattern for streaming
1072 *
1073 * Free the compiled pattern for streaming
1074 */
1075static void
1076xmlFreeStreamComp(xmlStreamCompPtr comp) {
1077 if (comp != NULL) {
1078 if (comp->steps != NULL)
1079 xmlFree(comp->steps);
1080 if (comp->dict != NULL)
1081 xmlDictFree(comp->dict);
1082 xmlFree(comp);
1083 }
1084}
1085
1086/**
1087 * xmlStreamCompAddStep:
1088 * @comp: the compiled pattern for streaming
1089 * @name: the first string, the name, or NULL for *
1090 * @ns: the second step, the namespace name
1091 * @flags: the flags for that step
1092 *
1093 * Add a new step to the compiled pattern
1094 *
1095 * Returns -1 in case of error or the step index if successful
1096 */
1097static int
1098xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1099 const xmlChar *ns, int flags) {
1100 xmlStreamStepPtr cur;
1101
1102 if (comp->nbStep >= comp->maxStep) {
1103 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1104 comp->maxStep * 2 * sizeof(xmlStreamStep));
1105 if (cur == NULL) {
1106 ERROR(NULL, NULL, NULL,
1107 "xmlNewStreamComp: malloc failed\n");
1108 return(-1);
1109 }
1110 comp->steps = cur;
1111 comp->maxStep *= 2;
1112 }
1113 cur = &comp->steps[comp->nbStep++];
1114 cur->flags = flags;
1115 cur->name = name;
1116 cur->ns = ns;
1117 return(comp->nbStep - 1);
1118}
1119
1120/**
1121 * xmlStreamCompile:
1122 * @comp: the precompiled pattern
1123 *
1124 * Tries to stream compile a pattern
1125 *
1126 * Returns -1 in case of failure and 0 in case of success.
1127 */
1128static int
1129xmlStreamCompile(xmlPatternPtr comp) {
1130 xmlStreamCompPtr stream;
1131 int i, s = 0, root = 0, desc = 0;
1132
1133 if ((comp == NULL) || (comp->steps == NULL))
1134 return(-1);
1135 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1136 if (stream == NULL)
1137 return(-1);
1138 if (comp->dict != NULL) {
1139 stream->dict = comp->dict;
1140 xmlDictReference(stream->dict);
1141 }
1142 for (i = 0;i < comp->nbStep;i++) {
1143 switch (comp->steps[i].op) {
1144 case XML_OP_END:
1145 break;
1146 case XML_OP_ROOT:
1147 if (i != 0)
1148 goto error;
1149 root = 1;
1150 break;
1151 case XML_OP_CHILD:
1152 case XML_OP_ATTR:
1153 case XML_OP_NS:
1154 goto error;
1155 case XML_OP_ELEM:
1156 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1157 comp->steps[i].value2, desc);
1158 desc = 0;
1159 if (s < 0)
1160 goto error;
1161 break;
1162 case XML_OP_ALL:
1163 s = xmlStreamCompAddStep(stream, NULL, NULL, desc);
1164 desc = 0;
1165 if (s < 0)
1166 goto error;
1167 break;
1168 case XML_OP_PARENT:
1169 break;
1170 case XML_OP_ANCESTOR:
1171 desc = XML_STREAM_STEP_DESC;
1172 break;
1173 }
1174 }
1175 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1176 if (root)
1177 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1178#ifdef DEBUG_STREAMING
1179 xmlDebugStreamComp(stream);
1180#endif
1181 comp->stream = stream;
1182 return(0);
1183error:
1184 xmlFreeStreamComp(stream);
1185 return(0);
1186}
1187
1188/**
1189 * xmlNewStreamCtxt:
1190 * @size: the number of expected states
1191 *
1192 * build a new stream context
1193 *
1194 * Returns the new structure or NULL in case of error.
1195 */
1196static xmlStreamCtxtPtr
1197xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1198 xmlStreamCtxtPtr cur;
1199
1200 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1201 if (cur == NULL) {
1202 ERROR(NULL, NULL, NULL,
1203 "xmlNewStreamCtxt: malloc failed\n");
1204 return(NULL);
1205 }
1206 memset(cur, 0, sizeof(xmlStreamCtxt));
1207 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1208 if (cur->states == NULL) {
1209 xmlFree(cur);
1210 ERROR(NULL, NULL, NULL,
1211 "xmlNewStreamCtxt: malloc failed\n");
1212 return(NULL);
1213 }
1214 cur->nbState = 0;
1215 cur->maxState = 4;
1216 cur->level = 0;
1217 cur->comp = stream;
1218 return(cur);
1219}
1220
1221/**
1222 * xmlFreeStreamCtxt:
1223 * @stream: the stream context
1224 *
1225 * Free the stream context
1226 */
1227void
1228xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1229 if (stream != NULL) {
1230 if (stream->states != NULL)
1231 xmlFree(stream->states);
1232 xmlFree(stream);
1233 }
1234}
1235
1236/**
1237 * xmlStreamCtxtAddState:
1238 * @comp: the stream context
1239 * @idx: the step index for that streaming state
1240 *
1241 * Add a new state to the stream context
1242 *
1243 * Returns -1 in case of error or the state index if successful
1244 */
1245static int
1246xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1247 int i;
1248 for (i = 0;i < comp->nbState;i++) {
1249 if (comp->states[2 * i] < 0) {
1250 comp->states[2 * i] = idx;
1251 comp->states[2 * i + 1] = level;
1252 return(i);
1253 }
1254 }
1255 if (comp->nbState >= comp->maxState) {
1256 int *cur;
1257
1258 cur = (int *) xmlRealloc(comp->states,
1259 comp->maxState * 4 * sizeof(int));
1260 if (cur == NULL) {
1261 ERROR(NULL, NULL, NULL,
1262 "xmlNewStreamCtxt: malloc failed\n");
1263 return(-1);
1264 }
1265 comp->states = cur;
1266 comp->maxState *= 2;
1267 }
1268 comp->states[2 * comp->nbState] = idx;
1269 comp->states[2 * comp->nbState++ + 1] = level;
1270 return(comp->nbState - 1);
1271}
1272
1273/**
1274 * xmlStreamPush:
1275 * @stream: the stream context
1276 * @name: the current name
1277 * @ns: the namespace name
1278 *
1279 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1280 * indicated a dictionnary, then strings for name and ns will be expected
1281 * to come from the dictionary.
1282 * Both @name and @ns being NULL means the / i.e. the root of the document.
1283 * This can also act as a reset.
1284 *
1285 * Returns: -1 in case of error, 1 if the current state in the stream is a
1286 * match and 0 otherwise.
1287 */
1288int
1289xmlStreamPush(xmlStreamCtxtPtr stream,
1290 const xmlChar *name, const xmlChar *ns) {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001291 int ret = 0, tmp, i, m, match, step, desc, final;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001292 xmlStreamCompPtr comp;
1293
1294 if ((stream == NULL) || (stream->nbState < 0))
1295 return(-1);
1296 comp = stream->comp;
1297 if ((name == NULL) && (ns == NULL)) {
1298 stream->nbState = 0;
1299 stream->level = 0;
1300 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1301 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1302 if (tmp < 0)
1303 return(-1);
1304 if (comp->steps[tmp].flags & XML_STREAM_STEP_FINAL)
1305 return(1);
1306 }
1307 return(0);
1308 }
1309 /*
1310 * Check evolution of existing states
1311 */
1312 m = stream->nbState;
1313 for (i = 0;i < m;i++) {
1314 match = 0;
1315 step = stream->states[2 * i];
1316 /* dead states */
1317 if (step < 0) continue;
1318 /* skip new states just added */
1319 if (stream->states[(2 * i) + 1] > stream->level) continue;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001320 /* skip continuations */
1321 desc = comp->steps[step].flags & XML_STREAM_STEP_DESC;
1322 if ((stream->states[(2 * i) + 1] < stream->level) && (!desc))continue;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001323
1324 /* discard old states */
1325 /* something needed about old level discarded */
1326
1327 if (comp->dict) {
1328 if (comp->steps[step].name == NULL) {
1329 if (comp->steps[step].ns == NULL)
1330 match = 1;
1331 else
1332 match = (comp->steps[step].ns == ns);
1333 } else {
1334 match = ((comp->steps[step].name == name) &&
1335 (comp->steps[step].ns == ns));
1336 }
1337 } else {
1338 if (comp->steps[step].name == NULL) {
1339 if (comp->steps[step].ns == NULL)
1340 match = 1;
1341 else
1342 match = xmlStrEqual(comp->steps[step].ns, ns);
1343 } else {
1344 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1345 (xmlStrEqual(comp->steps[step].ns, ns)));
1346 }
1347 }
1348 if (match) {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001349 final = comp->steps[step].flags & XML_STREAM_STEP_FINAL;
1350 if (desc) {
1351 if (final) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001352 ret = 1;
1353 } else {
1354 /* descending match create a new state */
1355 xmlStreamCtxtAddState(stream, step + 1, stream->level + 1);
1356 }
1357 } else {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001358 if (final) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001359 ret = 1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001360#if 0
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001361 stream->states[2 * i] = -1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001362#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001363 } else {
Daniel Veillard16ef8002005-01-31 00:27:50 +00001364#if 0
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001365 stream->states[2 * i] = step + 1;
1366 stream->states[2 * i + 1] = stream->level + 1;
Daniel Veillard16ef8002005-01-31 00:27:50 +00001367#endif
1368 xmlStreamCtxtAddState(stream, step + 1, stream->level + 1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001369 }
1370 }
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001371#if 0
Daniel Veillard16ef8002005-01-31 00:27:50 +00001372 } else if (!desc) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001373 /* didn't match, discard */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001374 stream->states[2 * i] = -1;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001375#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001376 }
1377 }
1378
1379 /*
1380 * Check creating a new state.
1381 */
1382 stream->level++;
1383 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
1384 match = 0;
1385 if (comp->dict) {
1386 if (comp->steps[0].name == NULL) {
1387 if (comp->steps[0].ns == NULL)
1388 match = 1;
1389 else
1390 match = (comp->steps[0].ns == ns);
1391 } else {
1392 match = ((comp->steps[0].name == name) &&
1393 (comp->steps[0].ns == ns));
1394 }
1395 } else {
1396 if (comp->steps[0].name == NULL) {
1397 if (comp->steps[0].ns == NULL)
1398 match = 1;
1399 else
1400 match = xmlStrEqual(comp->steps[0].ns, ns);
1401 } else {
1402 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1403 (xmlStrEqual(comp->steps[0].ns, ns)));
1404 }
1405 }
1406 if (match) {
1407 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1408 ret = 1;
1409 else
1410 xmlStreamCtxtAddState(stream, 1, stream->level);
1411 }
1412 }
1413
1414#ifdef DEBUG_STREAMING
1415 xmlDebugStreamCtxt(stream, ret);
1416#endif
1417 return(ret);
1418}
1419
1420/**
1421 * xmlStreamPop:
1422 * @stream: the stream context
1423 *
1424 * push one level from the stream.
1425 *
1426 * Returns: -1 in case of error, 0 otherwise.
1427 */
1428int
1429xmlStreamPop(xmlStreamCtxtPtr stream) {
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001430 int i, m;
1431
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001432 if (stream == NULL)
1433 return(-1);
1434 stream->level--;
1435 if (stream->level < 0)
1436 return(-1);
1437
Daniel Veillard9740d1d2005-02-01 16:21:43 +00001438 /*
1439 * Check evolution of existing states
1440 */
1441 m = stream->nbState;
1442 for (i = 0;i < m;i++) {
1443 if (stream->states[(2 * i)] < 0) break;
1444 /* discard obsoleted states */
1445 if (stream->states[(2 * i) + 1] > stream->level)
1446 stream->states[(2 * i)] = -1;
1447 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001448 return(0);
1449}
1450
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001451/************************************************************************
1452 * *
1453 * The public interfaces *
1454 * *
1455 ************************************************************************/
1456
1457/**
1458 * xmlPatterncompile:
1459 * @pattern: the pattern to compile
1460 * @dict: an optional dictionnary for interned strings
1461 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001462 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001463 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001464 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001465 *
1466 * Returns the compiled for of the pattern or NULL in case of error
1467 */
1468xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001469xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1470 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001471 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001472 xmlPatternPtr ret = NULL;
1473 xmlPatParserContextPtr ctxt = NULL;
1474
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001475 ctxt = xmlNewPatParserContext(pattern, dict, namespaces);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001476 if (ctxt == NULL) goto error;
1477 ret = xmlNewPattern();
1478 if (ret == NULL) goto error;
1479 ctxt->comp = ret;
1480
1481 xmlCompilePathPattern(ctxt);
1482 xmlFreePatParserContext(ctxt);
1483
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001484 xmlStreamCompile(ret);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +00001485 if (xmlReversePattern(ret) < 0)
1486 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001487 return(ret);
1488error:
1489 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1490 if (ret != NULL) xmlFreePattern(ret);
1491 return(NULL);
1492}
1493
1494/**
1495 * xmlPatternMatch:
1496 * @comp: the precompiled pattern
1497 * @node: a node
1498 *
1499 * Test wether the node matches the pattern
1500 *
1501 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1502 */
1503int
1504xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1505{
1506 if ((comp == NULL) || (node == NULL))
1507 return(-1);
1508 return(xmlPatMatch(comp, node));
1509}
1510
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001511/**
1512 * xmlPatternGetStreamCtxt:
1513 * @comp: the precompiled pattern
1514 *
1515 * Get a streaming context for that pattern
1516 * Use xmlFreeStreamCtxt to free the context.
1517 *
1518 * Returns a pointer to the context or NULL in case of failure
1519 */
1520xmlStreamCtxtPtr
1521xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1522{
1523 if ((comp == NULL) || (comp->stream == NULL))
1524 return(NULL);
1525 return(xmlNewStreamCtxt(comp->stream));
1526}
1527
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001528#endif /* LIBXML_PATTERN_ENABLED */