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