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