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