blob: e0f1566a95c54bb4a7e31fc8c006a03ec27a559b [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
28#define ERROR(a, b, c, d)
29#define ERROR5(a, b, c, d, e)
30
31/*
32 * Types are private:
33 */
34
35typedef enum {
36 XML_OP_END=0,
37 XML_OP_ROOT,
38 XML_OP_ELEM,
39 XML_OP_CHILD,
40 XML_OP_ATTR,
41 XML_OP_PARENT,
42 XML_OP_ANCESTOR,
43 XML_OP_NS,
44 XML_OP_ALL
45} xmlPatOp;
46
47
48typedef struct _xmlStepOp xmlStepOp;
49typedef xmlStepOp *xmlStepOpPtr;
50struct _xmlStepOp {
51 xmlPatOp op;
52 const xmlChar *value;
53 const xmlChar *value2;
54};
55
56struct _xmlPattern {
57 void *data; /* the associated template */
58 struct _xmlPattern *next; /* siblings */
59 const xmlChar *pattern; /* the pattern */
60
61 /* TODO fix the statically allocated size steps[] */
62 int nbStep;
63 int maxStep;
64 xmlStepOp steps[10]; /* ops for computation */
65};
66
67typedef struct _xmlPatParserContext xmlPatParserContext;
68typedef xmlPatParserContext *xmlPatParserContextPtr;
69struct _xmlPatParserContext {
70 const xmlChar *cur; /* the current char being parsed */
71 const xmlChar *base; /* the full expression */
72 int error; /* error code */
73 xmlDictPtr dict; /* the dictionnary if any */
74 xmlPatternPtr comp; /* the result */
75 xmlNodePtr elem; /* the current node if any */
76};
77
78/************************************************************************
79 * *
80 * Type functions *
81 * *
82 ************************************************************************/
83
84/**
85 * xmlNewPattern:
86 *
87 * Create a new XSLT Pattern
88 *
89 * Returns the newly allocated xmlPatternPtr or NULL in case of error
90 */
91static xmlPatternPtr
92xmlNewPattern(void) {
93 xmlPatternPtr cur;
94
95 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
96 if (cur == NULL) {
97 ERROR(NULL, NULL, NULL,
98 "xmlNewPattern : malloc failed\n");
99 return(NULL);
100 }
101 memset(cur, 0, sizeof(xmlPattern));
102 cur->maxStep = 10;
103 return(cur);
104}
105
106/**
107 * xmlFreePattern:
108 * @comp: an XSLT comp
109 *
110 * Free up the memory allocated by @comp
111 */
112void
113xmlFreePattern(xmlPatternPtr comp) {
114 xmlStepOpPtr op;
115 int i;
116
117 if (comp == NULL)
118 return;
119 if (comp->pattern != NULL)
120 xmlFree((xmlChar *)comp->pattern);
121 for (i = 0;i < comp->nbStep;i++) {
122 op = &comp->steps[i];
123 if (op->value != NULL)
124 xmlFree((xmlChar *) op->value);
125 if (op->value2 != NULL)
126 xmlFree((xmlChar *) op->value2);
127 }
128 memset(comp, -1, sizeof(xmlPattern));
129 xmlFree(comp);
130}
131
132/**
133 * xmlFreePatternList:
134 * @comp: an XSLT comp list
135 *
136 * Free up the memory allocated by all the elements of @comp
137 */
138void
139xmlFreePatternList(xmlPatternPtr comp) {
140 xmlPatternPtr cur;
141
142 while (comp != NULL) {
143 cur = comp;
144 comp = comp->next;
145 xmlFreePattern(cur);
146 }
147}
148
149/**
150 * xmlNewPatParserContext:
151 * @pattern: the pattern context
152 * @ctxt: the transformation context, if done at run-time
153 *
154 * Create a new XML pattern parser context
155 *
156 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
157 */
158static xmlPatParserContextPtr
159xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict) {
160 xmlPatParserContextPtr cur;
161
162 if (pattern == NULL)
163 return(NULL);
164
165 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
166 if (cur == NULL) {
167 ERROR(NULL, NULL, NULL,
168 "xmlNewPatParserContext : malloc failed\n");
169 return(NULL);
170 }
171 memset(cur, 0, sizeof(xmlPatParserContext));
172 cur->dict = dict;
173 cur->cur = pattern;
174 cur->base = pattern;
175 return(cur);
176}
177
178/**
179 * xmlFreePatParserContext:
180 * @ctxt: an XSLT parser context
181 *
182 * Free up the memory allocated by @ctxt
183 */
184static void
185xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
186 if (ctxt == NULL)
187 return;
188 memset(ctxt, -1, sizeof(xmlPatParserContext));
189 xmlFree(ctxt);
190}
191
192/**
193 * xmlPatternAdd:
194 * @comp: the compiled match expression
195 * @op: an op
196 * @value: the first value
197 * @value2: the second value
198 *
199 * Add an step to an XSLT Compiled Match
200 *
201 * Returns -1 in case of failure, 0 otherwise.
202 */
203static int
204xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
205 xmlPatternPtr comp,
206 xmlPatOp op, xmlChar * value, xmlChar * value2)
207{
208 if (comp->nbStep >= 10) {
209 ERROR(ctxt, NULL, NULL,
210 "xmlPatternAdd: overflow\n");
211 return (-1);
212 }
213 comp->steps[comp->nbStep].op = op;
214 comp->steps[comp->nbStep].value = value;
215 comp->steps[comp->nbStep].value2 = value2;
216 comp->nbStep++;
217 return (0);
218}
219
220#if 0
221/**
222 * xsltSwapTopPattern:
223 * @comp: the compiled match expression
224 *
225 * reverse the two top steps.
226 */
227static void
228xsltSwapTopPattern(xmlPatternPtr comp) {
229 int i;
230 int j = comp->nbStep - 1;
231
232 if (j > 0) {
233 register const xmlChar *tmp;
234 register xmlPatOp op;
235 i = j - 1;
236 tmp = comp->steps[i].value;
237 comp->steps[i].value = comp->steps[j].value;
238 comp->steps[j].value = tmp;
239 tmp = comp->steps[i].value2;
240 comp->steps[i].value2 = comp->steps[j].value2;
241 comp->steps[j].value2 = tmp;
242 op = comp->steps[i].op;
243 comp->steps[i].op = comp->steps[j].op;
244 comp->steps[j].op = op;
245 }
246}
247#endif
248
249/**
250 * xmlReversePattern:
251 * @comp: the compiled match expression
252 *
253 * reverse all the stack of expressions
254 */
255static void
256xmlReversePattern(xmlPatternPtr comp) {
257 int i = 0;
258 int j = comp->nbStep - 1;
259
260 while (j > i) {
261 register const xmlChar *tmp;
262 register xmlPatOp op;
263 tmp = comp->steps[i].value;
264 comp->steps[i].value = comp->steps[j].value;
265 comp->steps[j].value = tmp;
266 tmp = comp->steps[i].value2;
267 comp->steps[i].value2 = comp->steps[j].value2;
268 comp->steps[j].value2 = tmp;
269 op = comp->steps[i].op;
270 comp->steps[i].op = comp->steps[j].op;
271 comp->steps[j].op = op;
272 j--;
273 i++;
274 }
275 comp->steps[comp->nbStep++].op = XML_OP_END;
276}
277
278/************************************************************************
279 * *
280 * The interpreter for the precompiled patterns *
281 * *
282 ************************************************************************/
283
284/**
285 * xmlPatMatch:
286 * @comp: the precompiled pattern
287 * @node: a node
288 *
289 * Test wether the node matches the pattern
290 *
291 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
292 */
293static int
294xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
295 int i;
296 xmlStepOpPtr step;
297
298 if ((comp == NULL) || (node == NULL)) return(-1);
299 for (i = 0;i < comp->nbStep;i++) {
300 step = &comp->steps[i];
301 switch (step->op) {
302 case XML_OP_END:
303 return(1);
304 case XML_OP_ROOT:
305 if ((node->type == XML_DOCUMENT_NODE) ||
306#ifdef LIBXML_DOCB_ENABLED
307 (node->type == XML_DOCB_DOCUMENT_NODE) ||
308#endif
309 (node->type == XML_HTML_DOCUMENT_NODE))
310 continue;
311 return(0);
312 case XML_OP_ELEM:
313 if (node->type != XML_ELEMENT_NODE)
314 return(0);
315 if (step->value == NULL)
316 continue;
317 if (step->value[0] != node->name[0])
318 return(0);
319 if (!xmlStrEqual(step->value, node->name))
320 return(0);
321
322 /* Namespace test */
323 if (node->ns == NULL) {
324 if (step->value2 != NULL)
325 return(0);
326 } else if (node->ns->href != NULL) {
327 if (step->value2 == NULL)
328 return(0);
329 if (!xmlStrEqual(step->value2, node->ns->href))
330 return(0);
331 }
332 continue;
333 case XML_OP_CHILD: {
334 xmlNodePtr lst;
335
336 if ((node->type != XML_ELEMENT_NODE) &&
337 (node->type != XML_DOCUMENT_NODE) &&
338#ifdef LIBXML_DOCB_ENABLED
339 (node->type != XML_DOCB_DOCUMENT_NODE) &&
340#endif
341 (node->type != XML_HTML_DOCUMENT_NODE))
342 return(0);
343
344 lst = node->children;
345
346 if (step->value != NULL) {
347 while (lst != NULL) {
348 if ((lst->type == XML_ELEMENT_NODE) &&
349 (step->value[0] == lst->name[0]) &&
350 (xmlStrEqual(step->value, lst->name)))
351 break;
352 lst = lst->next;
353 }
354 if (lst != NULL)
355 continue;
356 }
357 return(0);
358 }
359 case XML_OP_ATTR:
360 if (node->type != XML_ATTRIBUTE_NODE)
361 return(0);
362 if (step->value != NULL) {
363 if (step->value[0] != node->name[0])
364 return(0);
365 if (!xmlStrEqual(step->value, node->name))
366 return(0);
367 }
368 /* Namespace test */
369 if (node->ns == NULL) {
370 if (step->value2 != NULL)
371 return(0);
372 } else if (step->value2 != NULL) {
373 if (!xmlStrEqual(step->value2, node->ns->href))
374 return(0);
375 }
376 continue;
377 case XML_OP_PARENT:
378 if ((node->type == XML_DOCUMENT_NODE) ||
379 (node->type == XML_HTML_DOCUMENT_NODE) ||
380#ifdef LIBXML_DOCB_ENABLED
381 (node->type == XML_DOCB_DOCUMENT_NODE) ||
382#endif
383 (node->type == XML_NAMESPACE_DECL))
384 return(0);
385 node = node->parent;
386 if (node == NULL)
387 return(0);
388 if (step->value == NULL)
389 continue;
390 if (step->value[0] != node->name[0])
391 return(0);
392 if (!xmlStrEqual(step->value, node->name))
393 return(0);
394 /* Namespace test */
395 if (node->ns == NULL) {
396 if (step->value2 != NULL)
397 return(0);
398 } else if (node->ns->href != NULL) {
399 if (step->value2 == NULL)
400 return(0);
401 if (!xmlStrEqual(step->value2, node->ns->href))
402 return(0);
403 }
404 continue;
405 case XML_OP_ANCESTOR:
406 /* TODO: implement coalescing of ANCESTOR/NODE ops */
407 if (step->value == NULL) {
408 i++;
409 step = &comp->steps[i];
410 if (step->op == XML_OP_ROOT)
411 return(1);
412 if (step->op != XML_OP_ELEM)
413 return(0);
414 if (step->value == NULL)
415 return(-1);
416 }
417 if (node == NULL)
418 return(0);
419 if ((node->type == XML_DOCUMENT_NODE) ||
420 (node->type == XML_HTML_DOCUMENT_NODE) ||
421#ifdef LIBXML_DOCB_ENABLED
422 (node->type == XML_DOCB_DOCUMENT_NODE) ||
423#endif
424 (node->type == XML_NAMESPACE_DECL))
425 return(0);
426 node = node->parent;
427 while (node != NULL) {
428 if (node == NULL)
429 return(0);
430 if ((node->type == XML_ELEMENT_NODE) &&
431 (step->value[0] == node->name[0]) &&
432 (xmlStrEqual(step->value, node->name))) {
433 /* Namespace test */
434 if (node->ns == NULL) {
435 if (step->value2 == NULL)
436 break;
437 } else if (node->ns->href != NULL) {
438 if ((step->value2 != NULL) &&
439 (xmlStrEqual(step->value2, node->ns->href)))
440 break;
441 }
442 }
443 node = node->parent;
444 }
445 if (node == NULL)
446 return(0);
447 continue;
448 case XML_OP_NS:
449 if (node->type != XML_ELEMENT_NODE)
450 return(0);
451 if (node->ns == NULL) {
452 if (step->value != NULL)
453 return(0);
454 } else if (node->ns->href != NULL) {
455 if (step->value == NULL)
456 return(0);
457 if (!xmlStrEqual(step->value, node->ns->href))
458 return(0);
459 }
460 break;
461 case XML_OP_ALL:
462 if (node->type != XML_ELEMENT_NODE)
463 return(0);
464 break;
465 }
466 }
467 return(1);
468}
469
470/************************************************************************
471 * *
472 * Dedicated parser for templates *
473 * *
474 ************************************************************************/
475
476#define TODO \
477 xmlGenericError(xmlGenericErrorContext, \
478 "Unimplemented block at %s:%d\n", \
479 __FILE__, __LINE__);
480#define CUR (*ctxt->cur)
481#define SKIP(val) ctxt->cur += (val)
482#define NXT(val) ctxt->cur[(val)]
483#define CUR_PTR ctxt->cur
484
485#define SKIP_BLANKS \
486 while (IS_BLANK(CUR)) NEXT
487
488#define CURRENT (*ctxt->cur)
489#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
490
491
492#define PUSH(op, val, val2) \
493 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
494
495#define XSLT_ERROR(X) \
496 { xsltError(ctxt, __FILE__, __LINE__, X); \
497 ctxt->error = (X); return; }
498
499#define XSLT_ERROR0(X) \
500 { xsltError(ctxt, __FILE__, __LINE__, X); \
501 ctxt->error = (X); return(0); }
502
503#if 0
504/**
505 * xmlPatScanLiteral:
506 * @ctxt: the XPath Parser context
507 *
508 * Parse an XPath Litteral:
509 *
510 * [29] Literal ::= '"' [^"]* '"'
511 * | "'" [^']* "'"
512 *
513 * Returns the Literal parsed or NULL
514 */
515
516static xmlChar *
517xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
518 const xmlChar *q, *cur;
519 xmlChar *ret = NULL;
520 int val, len;
521
522 SKIP_BLANKS;
523 if (CUR == '"') {
524 NEXT;
525 cur = q = CUR_PTR;
526 val = xmlStringCurrentChar(NULL, cur, &len);
527 while ((IS_CHAR(val)) && (val != '"')) {
528 cur += len;
529 val = xmlStringCurrentChar(NULL, cur, &len);
530 }
531 if (!IS_CHAR(val)) {
532 ctxt->error = 1;
533 return(NULL);
534 } else {
535 ret = xmlStrndup(q, cur - q);
536 }
537 cur += len;
538 CUR_PTR = cur;
539 } else if (CUR == '\'') {
540 NEXT;
541 cur = q = CUR_PTR;
542 val = xmlStringCurrentChar(NULL, cur, &len);
543 while ((IS_CHAR(val)) && (val != '\'')) {
544 cur += len;
545 val = xmlStringCurrentChar(NULL, cur, &len);
546 }
547 if (!IS_CHAR(val)) {
548 ctxt->error = 1;
549 return(NULL);
550 } else {
551 ret = xmlStrndup(q, cur - q);
552 }
553 cur += len;
554 CUR_PTR = cur;
555 } else {
556 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
557 ctxt->error = 1;
558 return(NULL);
559 }
560 return(ret);
561}
562#endif
563
564/**
565 * xmlPatScanName:
566 * @ctxt: the XPath Parser context
567 *
568 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
569 * CombiningChar | Extender
570 *
571 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
572 *
573 * [6] Names ::= Name (S Name)*
574 *
575 * Returns the Name parsed or NULL
576 */
577
578static xmlChar *
579xmlPatScanName(xmlPatParserContextPtr ctxt) {
580 const xmlChar *q, *cur;
581 xmlChar *ret = NULL;
582 int val, len;
583
584 SKIP_BLANKS;
585
586 cur = q = CUR_PTR;
587 val = xmlStringCurrentChar(NULL, cur, &len);
588 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
589 return(NULL);
590
591 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
592 (val == '.') || (val == '-') ||
593 (val == '_') ||
594 (IS_COMBINING(val)) ||
595 (IS_EXTENDER(val))) {
596 cur += len;
597 val = xmlStringCurrentChar(NULL, cur, &len);
598 }
599 ret = xmlStrndup(q, cur - q);
600 CUR_PTR = cur;
601 return(ret);
602}
603
604/**
605 * xmlPatScanNCName:
606 * @ctxt: the XPath Parser context
607 *
608 * Parses a non qualified name
609 *
610 * Returns the Name parsed or NULL
611 */
612
613static xmlChar *
614xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
615 const xmlChar *q, *cur;
616 xmlChar *ret = NULL;
617 int val, len;
618
619 SKIP_BLANKS;
620
621 cur = q = CUR_PTR;
622 val = xmlStringCurrentChar(NULL, cur, &len);
623 if (!IS_LETTER(val) && (val != '_'))
624 return(NULL);
625
626 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
627 (val == '.') || (val == '-') ||
628 (val == '_') ||
629 (IS_COMBINING(val)) ||
630 (IS_EXTENDER(val))) {
631 cur += len;
632 val = xmlStringCurrentChar(NULL, cur, &len);
633 }
634 ret = xmlStrndup(q, cur - q);
635 CUR_PTR = cur;
636 return(ret);
637}
638
639#if 0
640/**
641 * xmlPatScanQName:
642 * @ctxt: the XPath Parser context
643 * @prefix: the place to store the prefix
644 *
645 * Parse a qualified name
646 *
647 * Returns the Name parsed or NULL
648 */
649
650static xmlChar *
651xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
652 xmlChar *ret = NULL;
653
654 *prefix = NULL;
655 ret = xmlPatScanNCName(ctxt);
656 if (CUR == ':') {
657 *prefix = ret;
658 NEXT;
659 ret = xmlPatScanNCName(ctxt);
660 }
661 return(ret);
662}
663#endif
664
665/**
666 * xmlCompileStepPattern:
667 * @ctxt: the compilation context
668 *
669 * Compile the Step Pattern and generates a precompiled
670 * form suitable for fast matching.
671 *
672 * [3] Step ::= '.' | NameTest
673 * [4] NameTest ::= QName | '*' | NCName ':' '*'
674 */
675
676static void
677xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
678 xmlChar *token = NULL;
679 xmlChar *name = NULL;
680 const xmlChar *URI = NULL;
681 xmlChar *URL = NULL;
682
683 SKIP_BLANKS;
684 if (CUR == '.') {
685 NEXT;
686 PUSH(XML_OP_ELEM, NULL, NULL);
687 return;
688 }
689 name = xmlPatScanNCName(ctxt);
690 if (name == NULL) {
691 if (CUR == '*') {
692 NEXT;
693 PUSH(XML_OP_ALL, NULL, NULL);
694 return;
695 } else {
696 ERROR(NULL, NULL, NULL,
697 "xmlCompileStepPattern : Name expected\n");
698 ctxt->error = 1;
699 return;
700 }
701 }
702 SKIP_BLANKS;
703 if (CUR == ':') {
704 NEXT;
705 if (CUR != ':') {
706 xmlChar *prefix = name;
707 xmlNsPtr ns;
708
709 /*
710 * This is a namespace match
711 */
712 token = xmlPatScanName(ctxt);
713 ns = xmlSearchNs(NULL, ctxt->elem, prefix);
714 if (ns == NULL) {
715 ERROR5(NULL, NULL, NULL,
716 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
717 prefix);
718 ctxt->error = 1;
719 goto error;
720 } else {
721 URL = xmlStrdup(ns->href);
722 }
723 xmlFree(prefix);
724 if (token == NULL) {
725 if (CUR == '*') {
726 NEXT;
727 PUSH(XML_OP_NS, URL, NULL);
728 } else {
729 ERROR(NULL, NULL, NULL,
730 "xmlCompileStepPattern : Name expected\n");
731 ctxt->error = 1;
732 goto error;
733 }
734 } else {
735 PUSH(XML_OP_ELEM, token, URL);
736 }
737 } else {
738 NEXT;
739 if (xmlStrEqual(token, (const xmlChar *) "child")) {
740 xmlFree(token);
741 token = xmlPatScanName(ctxt);
742 if (token == NULL) {
743 if (CUR == '*') {
744 NEXT;
745 PUSH(XML_OP_ALL, token, NULL);
746 return;
747 } else {
748 ERROR(NULL, NULL, NULL,
749 "xmlCompileStepPattern : QName expected\n");
750 ctxt->error = 1;
751 goto error;
752 }
753 }
754 TODO
755 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
756 if (token == NULL) {
757 ctxt->error = 1;
758 goto error;
759 } else {
760 name = xmlStrdup(token);
761 if (URI != NULL)
762 URL = xmlStrdup(URI);
763 }
764 PUSH(XML_OP_CHILD, name, URL);
765 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
766 xmlFree(token);
767 token = xmlPatScanName(ctxt);
768 if (token == NULL) {
769 ERROR(NULL, NULL, NULL,
770 "xmlCompileStepPattern : QName expected\n");
771 ctxt->error = 1;
772 goto error;
773 }
774 TODO
775 /* URI = xsltGetQNameURI(ctxt->elem, &token); */
776 if (token == NULL) {
777 ctxt->error = 1;
778 goto error;
779 } else {
780 name = xmlStrdup(token);
781 if (URI != NULL)
782 URL = xmlStrdup(URI);
783 }
784 PUSH(XML_OP_ATTR, name, URL);
785 } else {
786 ERROR(NULL, NULL, NULL,
787 "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
788 ctxt->error = 1;
789 goto error;
790 }
791 xmlFree(token);
792 }
793 } else if (CUR == '*') {
794 NEXT;
795 PUSH(XML_OP_ALL, token, NULL);
796 } else {
797 if (name == NULL) {
798 ctxt->error = 1;
799 goto error;
800 }
801 PUSH(XML_OP_ELEM, name, NULL);
802 }
803 return;
804error:
805 if (token != NULL)
806 xmlFree(token);
807 if (name != NULL)
808 xmlFree(name);
809}
810
811/**
812 * xmlCompilePathPattern:
813 * @ctxt: the compilation context
814 *
815 * Compile the Path Pattern and generates a precompiled
816 * form suitable for fast matching.
817 *
818 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
819 */
820static void
821xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
822 SKIP_BLANKS;
823 if ((CUR == '/') && (NXT(1) == '/')) {
824 /*
825 * since we reverse the query
826 * a leading // can be safely ignored
827 */
828 NEXT;
829 NEXT;
830 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
831 /*
832 * a leading .// can be safely ignored
833 */
834 NEXT;
835 NEXT;
836 NEXT;
837 }
838 if (CUR == '@') {
839 TODO
840 } else {
841 xmlCompileStepPattern(ctxt);
842 SKIP_BLANKS;
843 while (CUR == '/') {
844 if ((CUR == '/') && (NXT(1) == '/')) {
845 PUSH(XML_OP_ANCESTOR, NULL, NULL);
846 NEXT;
847 NEXT;
848 SKIP_BLANKS;
849 xmlCompileStepPattern(ctxt);
850 } else {
851 PUSH(XML_OP_PARENT, NULL, NULL);
852 NEXT;
853 SKIP_BLANKS;
854 if ((CUR != 0) || (CUR == '|')) {
855 xmlCompileStepPattern(ctxt);
856 }
857 }
858 }
859 }
860error:
861 return;
862}
863/************************************************************************
864 * *
865 * The public interfaces *
866 * *
867 ************************************************************************/
868
869/**
870 * xmlPatterncompile:
871 * @pattern: the pattern to compile
872 * @dict: an optional dictionnary for interned strings
873 * @flags: compilation flags, undefined yet
874 *
875 * Compile a pattern
876 *
877 * Returns the compiled for of the pattern or NULL in case of error
878 */
879xmlPatternPtr
880xmlPatterncompile(const xmlChar *pattern, xmlDict *dictionnary, int flags) {
881 xmlPatternPtr ret = NULL;
882 xmlPatParserContextPtr ctxt = NULL;
883
884 ctxt = xmlNewPatParserContext(pattern, dictionnary);
885 if (ctxt == NULL) goto error;
886 ret = xmlNewPattern();
887 if (ret == NULL) goto error;
888 ctxt->comp = ret;
889
890 xmlCompilePathPattern(ctxt);
891 xmlFreePatParserContext(ctxt);
892
893 xmlReversePattern(ret);
894 return(ret);
895error:
896 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
897 if (ret != NULL) xmlFreePattern(ret);
898 return(NULL);
899}
900
901/**
902 * xmlPatternMatch:
903 * @comp: the precompiled pattern
904 * @node: a node
905 *
906 * Test wether the node matches the pattern
907 *
908 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
909 */
910int
911xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
912{
913 if ((comp == NULL) || (node == NULL))
914 return(-1);
915 return(xmlPatMatch(comp, node));
916}
917
918#endif /* LIBXML_PATTERN_ENABLED */