blob: 3ad56a15ea01b70b37ece0643e705298191941ca [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
40#ifdef LIBXML_PATTERN_ENABLED
41
Daniel Veillard8f2efae2005-01-30 18:44:30 +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 }
1032 printf("\n");
1033 }
1034}
1035#endif
1036/**
1037 * xmlNewStreamComp:
1038 * @size: the number of expected steps
1039 *
1040 * build a new compiled pattern for streaming
1041 *
1042 * Returns the new structure or NULL in case of error.
1043 */
1044static xmlStreamCompPtr
1045xmlNewStreamComp(int size) {
1046 xmlStreamCompPtr cur;
1047
1048 if (size < 4)
1049 size = 4;
1050
1051 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1052 if (cur == NULL) {
1053 ERROR(NULL, NULL, NULL,
1054 "xmlNewStreamComp: malloc failed\n");
1055 return(NULL);
1056 }
1057 memset(cur, 0, sizeof(xmlStreamComp));
1058 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1059 if (cur->steps == NULL) {
1060 xmlFree(cur);
1061 ERROR(NULL, NULL, NULL,
1062 "xmlNewStreamComp: malloc failed\n");
1063 return(NULL);
1064 }
1065 cur->nbStep = 0;
1066 cur->maxStep = size;
1067 return(cur);
1068}
1069
1070/**
1071 * xmlFreeStreamComp:
1072 * @comp: the compiled pattern for streaming
1073 *
1074 * Free the compiled pattern for streaming
1075 */
1076static void
1077xmlFreeStreamComp(xmlStreamCompPtr comp) {
1078 if (comp != NULL) {
1079 if (comp->steps != NULL)
1080 xmlFree(comp->steps);
1081 if (comp->dict != NULL)
1082 xmlDictFree(comp->dict);
1083 xmlFree(comp);
1084 }
1085}
1086
1087/**
1088 * xmlStreamCompAddStep:
1089 * @comp: the compiled pattern for streaming
1090 * @name: the first string, the name, or NULL for *
1091 * @ns: the second step, the namespace name
1092 * @flags: the flags for that step
1093 *
1094 * Add a new step to the compiled pattern
1095 *
1096 * Returns -1 in case of error or the step index if successful
1097 */
1098static int
1099xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1100 const xmlChar *ns, int flags) {
1101 xmlStreamStepPtr cur;
1102
1103 if (comp->nbStep >= comp->maxStep) {
1104 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1105 comp->maxStep * 2 * sizeof(xmlStreamStep));
1106 if (cur == NULL) {
1107 ERROR(NULL, NULL, NULL,
1108 "xmlNewStreamComp: malloc failed\n");
1109 return(-1);
1110 }
1111 comp->steps = cur;
1112 comp->maxStep *= 2;
1113 }
1114 cur = &comp->steps[comp->nbStep++];
1115 cur->flags = flags;
1116 cur->name = name;
1117 cur->ns = ns;
1118 return(comp->nbStep - 1);
1119}
1120
1121/**
1122 * xmlStreamCompile:
1123 * @comp: the precompiled pattern
1124 *
1125 * Tries to stream compile a pattern
1126 *
1127 * Returns -1 in case of failure and 0 in case of success.
1128 */
1129static int
1130xmlStreamCompile(xmlPatternPtr comp) {
1131 xmlStreamCompPtr stream;
1132 int i, s = 0, root = 0, desc = 0;
1133
1134 if ((comp == NULL) || (comp->steps == NULL))
1135 return(-1);
1136 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1137 if (stream == NULL)
1138 return(-1);
1139 if (comp->dict != NULL) {
1140 stream->dict = comp->dict;
1141 xmlDictReference(stream->dict);
1142 }
1143 for (i = 0;i < comp->nbStep;i++) {
1144 switch (comp->steps[i].op) {
1145 case XML_OP_END:
1146 break;
1147 case XML_OP_ROOT:
1148 if (i != 0)
1149 goto error;
1150 root = 1;
1151 break;
1152 case XML_OP_CHILD:
1153 case XML_OP_ATTR:
1154 case XML_OP_NS:
1155 goto error;
1156 case XML_OP_ELEM:
1157 s = xmlStreamCompAddStep(stream, comp->steps[i].value,
1158 comp->steps[i].value2, desc);
1159 desc = 0;
1160 if (s < 0)
1161 goto error;
1162 break;
1163 case XML_OP_ALL:
1164 s = xmlStreamCompAddStep(stream, NULL, NULL, desc);
1165 desc = 0;
1166 if (s < 0)
1167 goto error;
1168 break;
1169 case XML_OP_PARENT:
1170 break;
1171 case XML_OP_ANCESTOR:
1172 desc = XML_STREAM_STEP_DESC;
1173 break;
1174 }
1175 }
1176 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1177 if (root)
1178 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1179#ifdef DEBUG_STREAMING
1180 xmlDebugStreamComp(stream);
1181#endif
1182 comp->stream = stream;
1183 return(0);
1184error:
1185 xmlFreeStreamComp(stream);
1186 return(0);
1187}
1188
1189/**
1190 * xmlNewStreamCtxt:
1191 * @size: the number of expected states
1192 *
1193 * build a new stream context
1194 *
1195 * Returns the new structure or NULL in case of error.
1196 */
1197static xmlStreamCtxtPtr
1198xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1199 xmlStreamCtxtPtr cur;
1200
1201 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1202 if (cur == NULL) {
1203 ERROR(NULL, NULL, NULL,
1204 "xmlNewStreamCtxt: malloc failed\n");
1205 return(NULL);
1206 }
1207 memset(cur, 0, sizeof(xmlStreamCtxt));
1208 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1209 if (cur->states == NULL) {
1210 xmlFree(cur);
1211 ERROR(NULL, NULL, NULL,
1212 "xmlNewStreamCtxt: malloc failed\n");
1213 return(NULL);
1214 }
1215 cur->nbState = 0;
1216 cur->maxState = 4;
1217 cur->level = 0;
1218 cur->comp = stream;
1219 return(cur);
1220}
1221
1222/**
1223 * xmlFreeStreamCtxt:
1224 * @stream: the stream context
1225 *
1226 * Free the stream context
1227 */
1228void
1229xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1230 if (stream != NULL) {
1231 if (stream->states != NULL)
1232 xmlFree(stream->states);
1233 xmlFree(stream);
1234 }
1235}
1236
1237/**
1238 * xmlStreamCtxtAddState:
1239 * @comp: the stream context
1240 * @idx: the step index for that streaming state
1241 *
1242 * Add a new state to the stream context
1243 *
1244 * Returns -1 in case of error or the state index if successful
1245 */
1246static int
1247xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1248 int i;
1249 for (i = 0;i < comp->nbState;i++) {
1250 if (comp->states[2 * i] < 0) {
1251 comp->states[2 * i] = idx;
1252 comp->states[2 * i + 1] = level;
1253 return(i);
1254 }
1255 }
1256 if (comp->nbState >= comp->maxState) {
1257 int *cur;
1258
1259 cur = (int *) xmlRealloc(comp->states,
1260 comp->maxState * 4 * sizeof(int));
1261 if (cur == NULL) {
1262 ERROR(NULL, NULL, NULL,
1263 "xmlNewStreamCtxt: malloc failed\n");
1264 return(-1);
1265 }
1266 comp->states = cur;
1267 comp->maxState *= 2;
1268 }
1269 comp->states[2 * comp->nbState] = idx;
1270 comp->states[2 * comp->nbState++ + 1] = level;
1271 return(comp->nbState - 1);
1272}
1273
1274/**
1275 * xmlStreamPush:
1276 * @stream: the stream context
1277 * @name: the current name
1278 * @ns: the namespace name
1279 *
1280 * push new data onto the stream. NOTE: if the call xmlPatterncompile()
1281 * indicated a dictionnary, then strings for name and ns will be expected
1282 * to come from the dictionary.
1283 * Both @name and @ns being NULL means the / i.e. the root of the document.
1284 * This can also act as a reset.
1285 *
1286 * Returns: -1 in case of error, 1 if the current state in the stream is a
1287 * match and 0 otherwise.
1288 */
1289int
1290xmlStreamPush(xmlStreamCtxtPtr stream,
1291 const xmlChar *name, const xmlChar *ns) {
1292 int ret = 0, tmp, i, m, match, step;
1293 xmlStreamCompPtr comp;
1294
1295 if ((stream == NULL) || (stream->nbState < 0))
1296 return(-1);
1297 comp = stream->comp;
1298 if ((name == NULL) && (ns == NULL)) {
1299 stream->nbState = 0;
1300 stream->level = 0;
1301 if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1302 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1303 if (tmp < 0)
1304 return(-1);
1305 if (comp->steps[tmp].flags & XML_STREAM_STEP_FINAL)
1306 return(1);
1307 }
1308 return(0);
1309 }
1310 /*
1311 * Check evolution of existing states
1312 */
1313 m = stream->nbState;
1314 for (i = 0;i < m;i++) {
1315 match = 0;
1316 step = stream->states[2 * i];
1317 /* dead states */
1318 if (step < 0) continue;
1319 /* skip new states just added */
1320 if (stream->states[(2 * i) + 1] > stream->level) continue;
1321
1322 /* discard old states */
1323 /* something needed about old level discarded */
1324
1325 if (comp->dict) {
1326 if (comp->steps[step].name == NULL) {
1327 if (comp->steps[step].ns == NULL)
1328 match = 1;
1329 else
1330 match = (comp->steps[step].ns == ns);
1331 } else {
1332 match = ((comp->steps[step].name == name) &&
1333 (comp->steps[step].ns == ns));
1334 }
1335 } else {
1336 if (comp->steps[step].name == NULL) {
1337 if (comp->steps[step].ns == NULL)
1338 match = 1;
1339 else
1340 match = xmlStrEqual(comp->steps[step].ns, ns);
1341 } else {
1342 match = ((xmlStrEqual(comp->steps[step].name, name)) &&
1343 (xmlStrEqual(comp->steps[step].ns, ns)));
1344 }
1345 }
1346 if (match) {
1347 if (comp->steps[step].flags & XML_STREAM_STEP_DESC) {
1348 if (comp->steps[step].flags & XML_STREAM_STEP_FINAL) {
1349 ret = 1;
1350 } else {
1351 /* descending match create a new state */
1352 xmlStreamCtxtAddState(stream, step + 1, stream->level + 1);
1353 }
1354 } else {
1355 if (comp->steps[step].flags & XML_STREAM_STEP_FINAL) {
1356 ret = 1;
1357 stream->states[2 * i] = -1;
1358 } else {
1359 stream->states[2 * i] = step + 1;
1360 stream->states[2 * i + 1] = stream->level + 1;
1361 }
1362 }
1363 } else if (!(comp->steps[step].flags & XML_STREAM_STEP_DESC)) {
1364 /* didn't match, discard */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001365 stream->states[2 * i] = -1;
1366 }
1367 }
1368
1369 /*
1370 * Check creating a new state.
1371 */
1372 stream->level++;
1373 if (!(comp->steps[0].flags & XML_STREAM_STEP_ROOT)) {
1374 match = 0;
1375 if (comp->dict) {
1376 if (comp->steps[0].name == NULL) {
1377 if (comp->steps[0].ns == NULL)
1378 match = 1;
1379 else
1380 match = (comp->steps[0].ns == ns);
1381 } else {
1382 match = ((comp->steps[0].name == name) &&
1383 (comp->steps[0].ns == ns));
1384 }
1385 } else {
1386 if (comp->steps[0].name == NULL) {
1387 if (comp->steps[0].ns == NULL)
1388 match = 1;
1389 else
1390 match = xmlStrEqual(comp->steps[0].ns, ns);
1391 } else {
1392 match = ((xmlStrEqual(comp->steps[0].name, name)) &&
1393 (xmlStrEqual(comp->steps[0].ns, ns)));
1394 }
1395 }
1396 if (match) {
1397 if (comp->steps[0].flags & XML_STREAM_STEP_FINAL)
1398 ret = 1;
1399 else
1400 xmlStreamCtxtAddState(stream, 1, stream->level);
1401 }
1402 }
1403
1404#ifdef DEBUG_STREAMING
1405 xmlDebugStreamCtxt(stream, ret);
1406#endif
1407 return(ret);
1408}
1409
1410/**
1411 * xmlStreamPop:
1412 * @stream: the stream context
1413 *
1414 * push one level from the stream.
1415 *
1416 * Returns: -1 in case of error, 0 otherwise.
1417 */
1418int
1419xmlStreamPop(xmlStreamCtxtPtr stream) {
1420 if (stream == NULL)
1421 return(-1);
1422 stream->level--;
1423 if (stream->level < 0)
1424 return(-1);
1425
1426 /* something is certainly needed here */
1427 return(0);
1428}
1429
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001430/************************************************************************
1431 * *
1432 * The public interfaces *
1433 * *
1434 ************************************************************************/
1435
1436/**
1437 * xmlPatterncompile:
1438 * @pattern: the pattern to compile
1439 * @dict: an optional dictionnary for interned strings
1440 * @flags: compilation flags, undefined yet
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001441 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001442 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001443 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001444 *
1445 * Returns the compiled for of the pattern or NULL in case of error
1446 */
1447xmlPatternPtr
Daniel Veillard427174f2003-12-10 10:42:59 +00001448xmlPatterncompile(const xmlChar *pattern, xmlDict *dict,
1449 int flags ATTRIBUTE_UNUSED,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001450 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001451 xmlPatternPtr ret = NULL;
1452 xmlPatParserContextPtr ctxt = NULL;
1453
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00001454 ctxt = xmlNewPatParserContext(pattern, dict, namespaces);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001455 if (ctxt == NULL) goto error;
1456 ret = xmlNewPattern();
1457 if (ret == NULL) goto error;
1458 ctxt->comp = ret;
1459
1460 xmlCompilePathPattern(ctxt);
1461 xmlFreePatParserContext(ctxt);
1462
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001463 xmlStreamCompile(ret);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +00001464 if (xmlReversePattern(ret) < 0)
1465 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001466 return(ret);
1467error:
1468 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
1469 if (ret != NULL) xmlFreePattern(ret);
1470 return(NULL);
1471}
1472
1473/**
1474 * xmlPatternMatch:
1475 * @comp: the precompiled pattern
1476 * @node: a node
1477 *
1478 * Test wether the node matches the pattern
1479 *
1480 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1481 */
1482int
1483xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
1484{
1485 if ((comp == NULL) || (node == NULL))
1486 return(-1);
1487 return(xmlPatMatch(comp, node));
1488}
1489
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001490/**
1491 * xmlPatternGetStreamCtxt:
1492 * @comp: the precompiled pattern
1493 *
1494 * Get a streaming context for that pattern
1495 * Use xmlFreeStreamCtxt to free the context.
1496 *
1497 * Returns a pointer to the context or NULL in case of failure
1498 */
1499xmlStreamCtxtPtr
1500xmlPatternGetStreamCtxt(xmlPatternPtr comp)
1501{
1502 if ((comp == NULL) || (comp->stream == NULL))
1503 return(NULL);
1504 return(xmlNewStreamCtxt(comp->stream));
1505}
1506
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001507#endif /* LIBXML_PATTERN_ENABLED */