Annotation of embedaddon/libxml2/pattern.c, revision 1.1.1.3
1.1 misho 1: /*
2: * pattern.c: Implemetation of selectors for nodes
3: *
4: * Reference:
5: * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
1.1.1.3 ! misho 6: * to some extent
1.1 misho 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: /*
15: * TODO:
16: * - compilation flags to check for specific syntaxes
17: * using flags of xmlPatterncompile()
18: * - making clear how pattern starting with / or . need to be handled,
19: * currently push(NULL, NULL) means a reset of the streaming context
20: * and indicating we are on / (the document node), probably need
21: * something similar for .
22: * - get rid of the "compile" starting with lowercase
23: * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary
24: */
25:
26: #define IN_LIBXML
27: #include "libxml.h"
28:
29: #include <string.h>
30: #include <libxml/xmlmemory.h>
31: #include <libxml/tree.h>
32: #include <libxml/hash.h>
33: #include <libxml/dict.h>
34: #include <libxml/xmlerror.h>
35: #include <libxml/parserInternals.h>
36: #include <libxml/pattern.h>
37:
38: #ifdef LIBXML_PATTERN_ENABLED
39:
40: /* #define DEBUG_STREAMING */
41:
1.1.1.2 misho 42: #ifdef ERROR
43: #undef ERROR
44: #endif
1.1 misho 45: #define ERROR(a, b, c, d)
46: #define ERROR5(a, b, c, d, e)
47:
48: #define XML_STREAM_STEP_DESC 1
49: #define XML_STREAM_STEP_FINAL 2
50: #define XML_STREAM_STEP_ROOT 4
51: #define XML_STREAM_STEP_ATTR 8
52: #define XML_STREAM_STEP_NODE 16
53: #define XML_STREAM_STEP_IN_SET 32
54:
55: /*
56: * NOTE: Those private flags (XML_STREAM_xxx) are used
57: * in _xmlStreamCtxt->flag. They extend the public
58: * xmlPatternFlags, so be carefull not to interfere with the
1.1.1.3 ! misho 59: * reserved values for xmlPatternFlags.
1.1 misho 60: */
61: #define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62: #define XML_STREAM_FROM_ROOT 1<<15
63: #define XML_STREAM_DESC 1<<16
64:
65: /*
66: * XML_STREAM_ANY_NODE is used for comparison against
67: * xmlElementType enums, to indicate a node of any type.
68: */
69: #define XML_STREAM_ANY_NODE 100
70:
71: #define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
72: XML_PATTERN_XSSEL | \
73: XML_PATTERN_XSFIELD)
74:
75: #define XML_STREAM_XS_IDC(c) ((c)->flags & \
76: (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
77:
78: #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
79:
80: #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
81:
82: #define XML_PAT_COPY_NSNAME(c, r, nsname) \
83: if ((c)->comp->dict) \
84: r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
85: else r = xmlStrdup(BAD_CAST nsname);
86:
87: #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
88:
89: typedef struct _xmlStreamStep xmlStreamStep;
90: typedef xmlStreamStep *xmlStreamStepPtr;
91: struct _xmlStreamStep {
92: int flags; /* properties of that step */
93: const xmlChar *name; /* first string value if NULL accept all */
94: const xmlChar *ns; /* second string value */
95: int nodeType; /* type of node */
96: };
97:
98: typedef struct _xmlStreamComp xmlStreamComp;
99: typedef xmlStreamComp *xmlStreamCompPtr;
100: struct _xmlStreamComp {
101: xmlDict *dict; /* the dictionary if any */
102: int nbStep; /* number of steps in the automata */
103: int maxStep; /* allocated number of steps */
104: xmlStreamStepPtr steps; /* the array of steps */
105: int flags;
106: };
107:
108: struct _xmlStreamCtxt {
109: struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
110: xmlStreamCompPtr comp; /* the compiled stream */
111: int nbState; /* number of states in the automata */
112: int maxState; /* allocated number of states */
113: int level; /* how deep are we ? */
114: int *states; /* the array of step indexes */
115: int flags; /* validation options */
116: int blockLevel;
117: };
118:
119: static void xmlFreeStreamComp(xmlStreamCompPtr comp);
120:
121: /*
122: * Types are private:
123: */
124:
125: typedef enum {
126: XML_OP_END=0,
127: XML_OP_ROOT,
128: XML_OP_ELEM,
129: XML_OP_CHILD,
130: XML_OP_ATTR,
131: XML_OP_PARENT,
132: XML_OP_ANCESTOR,
133: XML_OP_NS,
134: XML_OP_ALL
135: } xmlPatOp;
136:
137:
138: typedef struct _xmlStepState xmlStepState;
139: typedef xmlStepState *xmlStepStatePtr;
140: struct _xmlStepState {
141: int step;
142: xmlNodePtr node;
143: };
144:
145: typedef struct _xmlStepStates xmlStepStates;
146: typedef xmlStepStates *xmlStepStatesPtr;
147: struct _xmlStepStates {
148: int nbstates;
149: int maxstates;
150: xmlStepStatePtr states;
151: };
152:
153: typedef struct _xmlStepOp xmlStepOp;
154: typedef xmlStepOp *xmlStepOpPtr;
155: struct _xmlStepOp {
156: xmlPatOp op;
157: const xmlChar *value;
158: const xmlChar *value2; /* The namespace name */
159: };
160:
161: #define PAT_FROM_ROOT (1<<8)
162: #define PAT_FROM_CUR (1<<9)
163:
164: struct _xmlPattern {
1.1.1.3 ! misho 165: void *data; /* the associated template */
1.1 misho 166: xmlDictPtr dict; /* the optional dictionary */
167: struct _xmlPattern *next; /* next pattern if | is used */
168: const xmlChar *pattern; /* the pattern */
169: int flags; /* flags */
170: int nbStep;
171: int maxStep;
172: xmlStepOpPtr steps; /* ops for computation */
173: xmlStreamCompPtr stream; /* the streaming data if any */
174: };
175:
176: typedef struct _xmlPatParserContext xmlPatParserContext;
177: typedef xmlPatParserContext *xmlPatParserContextPtr;
178: struct _xmlPatParserContext {
179: const xmlChar *cur; /* the current char being parsed */
180: const xmlChar *base; /* the full expression */
181: int error; /* error code */
182: xmlDictPtr dict; /* the dictionary if any */
183: xmlPatternPtr comp; /* the result */
1.1.1.3 ! misho 184: xmlNodePtr elem; /* the current node if any */
1.1 misho 185: const xmlChar **namespaces; /* the namespaces definitions */
186: int nb_namespaces; /* the number of namespaces */
187: };
188:
189: /************************************************************************
1.1.1.3 ! misho 190: * *
! 191: * Type functions *
! 192: * *
1.1 misho 193: ************************************************************************/
194:
195: /**
196: * xmlNewPattern:
197: *
198: * Create a new XSLT Pattern
199: *
200: * Returns the newly allocated xmlPatternPtr or NULL in case of error
201: */
202: static xmlPatternPtr
203: xmlNewPattern(void) {
204: xmlPatternPtr cur;
205:
206: cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
207: if (cur == NULL) {
208: ERROR(NULL, NULL, NULL,
209: "xmlNewPattern : malloc failed\n");
210: return(NULL);
211: }
212: memset(cur, 0, sizeof(xmlPattern));
213: cur->maxStep = 10;
214: cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
215: if (cur->steps == NULL) {
216: xmlFree(cur);
217: ERROR(NULL, NULL, NULL,
218: "xmlNewPattern : malloc failed\n");
219: return(NULL);
220: }
221: return(cur);
222: }
223:
224: /**
225: * xmlFreePattern:
226: * @comp: an XSLT comp
227: *
228: * Free up the memory allocated by @comp
229: */
230: void
231: xmlFreePattern(xmlPatternPtr comp) {
232: xmlStepOpPtr op;
233: int i;
234:
235: if (comp == NULL)
236: return;
237: if (comp->next != NULL)
238: xmlFreePattern(comp->next);
239: if (comp->stream != NULL)
240: xmlFreeStreamComp(comp->stream);
241: if (comp->pattern != NULL)
242: xmlFree((xmlChar *)comp->pattern);
243: if (comp->steps != NULL) {
244: if (comp->dict == NULL) {
245: for (i = 0;i < comp->nbStep;i++) {
246: op = &comp->steps[i];
247: if (op->value != NULL)
248: xmlFree((xmlChar *) op->value);
249: if (op->value2 != NULL)
250: xmlFree((xmlChar *) op->value2);
251: }
252: }
253: xmlFree(comp->steps);
254: }
255: if (comp->dict != NULL)
256: xmlDictFree(comp->dict);
257:
258: memset(comp, -1, sizeof(xmlPattern));
259: xmlFree(comp);
260: }
261:
262: /**
263: * xmlFreePatternList:
264: * @comp: an XSLT comp list
265: *
266: * Free up the memory allocated by all the elements of @comp
267: */
268: void
269: xmlFreePatternList(xmlPatternPtr comp) {
270: xmlPatternPtr cur;
271:
272: while (comp != NULL) {
273: cur = comp;
274: comp = comp->next;
275: cur->next = NULL;
276: xmlFreePattern(cur);
277: }
278: }
279:
280: /**
281: * xmlNewPatParserContext:
282: * @pattern: the pattern context
283: * @dict: the inherited dictionary or NULL
284: * @namespaces: the prefix definitions, array of [URI, prefix] terminated
285: * with [NULL, NULL] or NULL if no namespace is used
286: *
287: * Create a new XML pattern parser context
288: *
289: * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
290: */
291: static xmlPatParserContextPtr
292: xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
293: const xmlChar **namespaces) {
294: xmlPatParserContextPtr cur;
295:
296: if (pattern == NULL)
297: return(NULL);
298:
299: cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
300: if (cur == NULL) {
301: ERROR(NULL, NULL, NULL,
302: "xmlNewPatParserContext : malloc failed\n");
303: return(NULL);
304: }
305: memset(cur, 0, sizeof(xmlPatParserContext));
306: cur->dict = dict;
307: cur->cur = pattern;
308: cur->base = pattern;
309: if (namespaces != NULL) {
310: int i;
1.1.1.2 misho 311: for (i = 0;namespaces[2 * i] != NULL;i++)
312: ;
1.1 misho 313: cur->nb_namespaces = i;
314: } else {
315: cur->nb_namespaces = 0;
316: }
317: cur->namespaces = namespaces;
318: return(cur);
319: }
320:
321: /**
322: * xmlFreePatParserContext:
323: * @ctxt: an XSLT parser context
324: *
325: * Free up the memory allocated by @ctxt
326: */
327: static void
328: xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
329: if (ctxt == NULL)
1.1.1.3 ! misho 330: return;
1.1 misho 331: memset(ctxt, -1, sizeof(xmlPatParserContext));
332: xmlFree(ctxt);
333: }
334:
335: /**
336: * xmlPatternAdd:
337: * @comp: the compiled match expression
338: * @op: an op
339: * @value: the first value
340: * @value2: the second value
341: *
342: * Add a step to an XSLT Compiled Match
343: *
344: * Returns -1 in case of failure, 0 otherwise.
345: */
346: static int
347: xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
348: xmlPatternPtr comp,
349: xmlPatOp op, xmlChar * value, xmlChar * value2)
350: {
351: if (comp->nbStep >= comp->maxStep) {
352: xmlStepOpPtr temp;
353: temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
354: sizeof(xmlStepOp));
355: if (temp == NULL) {
356: ERROR(ctxt, NULL, NULL,
357: "xmlPatternAdd: realloc failed\n");
358: return (-1);
359: }
360: comp->steps = temp;
361: comp->maxStep *= 2;
362: }
363: comp->steps[comp->nbStep].op = op;
364: comp->steps[comp->nbStep].value = value;
365: comp->steps[comp->nbStep].value2 = value2;
366: comp->nbStep++;
367: return (0);
368: }
369:
370: #if 0
371: /**
372: * xsltSwapTopPattern:
373: * @comp: the compiled match expression
374: *
375: * reverse the two top steps.
376: */
377: static void
378: xsltSwapTopPattern(xmlPatternPtr comp) {
379: int i;
380: int j = comp->nbStep - 1;
381:
382: if (j > 0) {
383: register const xmlChar *tmp;
384: register xmlPatOp op;
385: i = j - 1;
386: tmp = comp->steps[i].value;
387: comp->steps[i].value = comp->steps[j].value;
388: comp->steps[j].value = tmp;
389: tmp = comp->steps[i].value2;
390: comp->steps[i].value2 = comp->steps[j].value2;
391: comp->steps[j].value2 = tmp;
392: op = comp->steps[i].op;
393: comp->steps[i].op = comp->steps[j].op;
394: comp->steps[j].op = op;
395: }
396: }
397: #endif
398:
399: /**
400: * xmlReversePattern:
401: * @comp: the compiled match expression
402: *
403: * reverse all the stack of expressions
404: *
405: * returns 0 in case of success and -1 in case of error.
406: */
407: static int
408: xmlReversePattern(xmlPatternPtr comp) {
409: int i, j;
410:
411: /*
412: * remove the leading // for //a or .//a
413: */
414: if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
415: for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
416: comp->steps[i].value = comp->steps[j].value;
417: comp->steps[i].value2 = comp->steps[j].value2;
418: comp->steps[i].op = comp->steps[j].op;
419: }
420: comp->nbStep--;
421: }
422: if (comp->nbStep >= comp->maxStep) {
423: xmlStepOpPtr temp;
424: temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
425: sizeof(xmlStepOp));
426: if (temp == NULL) {
427: ERROR(ctxt, NULL, NULL,
428: "xmlReversePattern: realloc failed\n");
429: return (-1);
430: }
431: comp->steps = temp;
432: comp->maxStep *= 2;
433: }
434: i = 0;
435: j = comp->nbStep - 1;
436: while (j > i) {
437: register const xmlChar *tmp;
438: register xmlPatOp op;
439: tmp = comp->steps[i].value;
440: comp->steps[i].value = comp->steps[j].value;
441: comp->steps[j].value = tmp;
442: tmp = comp->steps[i].value2;
443: comp->steps[i].value2 = comp->steps[j].value2;
444: comp->steps[j].value2 = tmp;
445: op = comp->steps[i].op;
446: comp->steps[i].op = comp->steps[j].op;
447: comp->steps[j].op = op;
448: j--;
449: i++;
450: }
451: comp->steps[comp->nbStep].value = NULL;
452: comp->steps[comp->nbStep].value2 = NULL;
453: comp->steps[comp->nbStep++].op = XML_OP_END;
454: return(0);
455: }
456:
457: /************************************************************************
1.1.1.3 ! misho 458: * *
! 459: * The interpreter for the precompiled patterns *
! 460: * *
1.1 misho 461: ************************************************************************/
462:
463: static int
464: xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
465: if ((states->states == NULL) || (states->maxstates <= 0)) {
466: states->maxstates = 4;
467: states->nbstates = 0;
468: states->states = xmlMalloc(4 * sizeof(xmlStepState));
469: }
470: else if (states->maxstates <= states->nbstates) {
471: xmlStepState *tmp;
472:
473: tmp = (xmlStepStatePtr) xmlRealloc(states->states,
474: 2 * states->maxstates * sizeof(xmlStepState));
475: if (tmp == NULL)
476: return(-1);
477: states->states = tmp;
478: states->maxstates *= 2;
479: }
480: states->states[states->nbstates].step = step;
481: states->states[states->nbstates++].node = node;
482: #if 0
483: fprintf(stderr, "Push: %d, %s\n", step, node->name);
484: #endif
485: return(0);
486: }
487:
488: /**
489: * xmlPatMatch:
490: * @comp: the precompiled pattern
491: * @node: a node
492: *
493: * Test whether the node matches the pattern
494: *
495: * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
496: */
497: static int
498: xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
499: int i;
500: xmlStepOpPtr step;
501: xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
502:
503: if ((comp == NULL) || (node == NULL)) return(-1);
504: i = 0;
505: restart:
506: for (;i < comp->nbStep;i++) {
507: step = &comp->steps[i];
508: switch (step->op) {
509: case XML_OP_END:
510: goto found;
511: case XML_OP_ROOT:
512: if (node->type == XML_NAMESPACE_DECL)
513: goto rollback;
514: node = node->parent;
515: if ((node->type == XML_DOCUMENT_NODE) ||
516: #ifdef LIBXML_DOCB_ENABLED
517: (node->type == XML_DOCB_DOCUMENT_NODE) ||
518: #endif
519: (node->type == XML_HTML_DOCUMENT_NODE))
520: continue;
521: goto rollback;
522: case XML_OP_ELEM:
523: if (node->type != XML_ELEMENT_NODE)
524: goto rollback;
525: if (step->value == NULL)
526: continue;
527: if (step->value[0] != node->name[0])
528: goto rollback;
529: if (!xmlStrEqual(step->value, node->name))
530: goto rollback;
531:
532: /* Namespace test */
533: if (node->ns == NULL) {
534: if (step->value2 != NULL)
535: goto rollback;
536: } else if (node->ns->href != NULL) {
537: if (step->value2 == NULL)
538: goto rollback;
539: if (!xmlStrEqual(step->value2, node->ns->href))
540: goto rollback;
541: }
542: continue;
543: case XML_OP_CHILD: {
544: xmlNodePtr lst;
545:
546: if ((node->type != XML_ELEMENT_NODE) &&
547: (node->type != XML_DOCUMENT_NODE) &&
548: #ifdef LIBXML_DOCB_ENABLED
549: (node->type != XML_DOCB_DOCUMENT_NODE) &&
550: #endif
551: (node->type != XML_HTML_DOCUMENT_NODE))
552: goto rollback;
553:
554: lst = node->children;
555:
556: if (step->value != NULL) {
557: while (lst != NULL) {
558: if ((lst->type == XML_ELEMENT_NODE) &&
559: (step->value[0] == lst->name[0]) &&
560: (xmlStrEqual(step->value, lst->name)))
561: break;
562: lst = lst->next;
563: }
564: if (lst != NULL)
565: continue;
566: }
567: goto rollback;
568: }
569: case XML_OP_ATTR:
570: if (node->type != XML_ATTRIBUTE_NODE)
571: goto rollback;
572: if (step->value != NULL) {
573: if (step->value[0] != node->name[0])
574: goto rollback;
575: if (!xmlStrEqual(step->value, node->name))
576: goto rollback;
577: }
578: /* Namespace test */
579: if (node->ns == NULL) {
580: if (step->value2 != NULL)
581: goto rollback;
582: } else if (step->value2 != NULL) {
583: if (!xmlStrEqual(step->value2, node->ns->href))
584: goto rollback;
585: }
586: continue;
587: case XML_OP_PARENT:
588: if ((node->type == XML_DOCUMENT_NODE) ||
589: (node->type == XML_HTML_DOCUMENT_NODE) ||
590: #ifdef LIBXML_DOCB_ENABLED
591: (node->type == XML_DOCB_DOCUMENT_NODE) ||
592: #endif
593: (node->type == XML_NAMESPACE_DECL))
594: goto rollback;
595: node = node->parent;
596: if (node == NULL)
597: goto rollback;
598: if (step->value == NULL)
599: continue;
600: if (step->value[0] != node->name[0])
601: goto rollback;
602: if (!xmlStrEqual(step->value, node->name))
603: goto rollback;
604: /* Namespace test */
605: if (node->ns == NULL) {
606: if (step->value2 != NULL)
607: goto rollback;
608: } else if (node->ns->href != NULL) {
609: if (step->value2 == NULL)
610: goto rollback;
611: if (!xmlStrEqual(step->value2, node->ns->href))
612: goto rollback;
613: }
614: continue;
615: case XML_OP_ANCESTOR:
616: /* TODO: implement coalescing of ANCESTOR/NODE ops */
617: if (step->value == NULL) {
618: i++;
619: step = &comp->steps[i];
620: if (step->op == XML_OP_ROOT)
621: goto found;
622: if (step->op != XML_OP_ELEM)
623: goto rollback;
624: if (step->value == NULL)
625: return(-1);
626: }
627: if (node == NULL)
628: goto rollback;
629: if ((node->type == XML_DOCUMENT_NODE) ||
630: (node->type == XML_HTML_DOCUMENT_NODE) ||
631: #ifdef LIBXML_DOCB_ENABLED
632: (node->type == XML_DOCB_DOCUMENT_NODE) ||
633: #endif
634: (node->type == XML_NAMESPACE_DECL))
635: goto rollback;
636: node = node->parent;
637: while (node != NULL) {
638: if ((node->type == XML_ELEMENT_NODE) &&
639: (step->value[0] == node->name[0]) &&
640: (xmlStrEqual(step->value, node->name))) {
641: /* Namespace test */
642: if (node->ns == NULL) {
643: if (step->value2 == NULL)
644: break;
645: } else if (node->ns->href != NULL) {
646: if ((step->value2 != NULL) &&
647: (xmlStrEqual(step->value2, node->ns->href)))
648: break;
649: }
650: }
651: node = node->parent;
652: }
653: if (node == NULL)
654: goto rollback;
655: /*
656: * prepare a potential rollback from here
657: * for ancestors of that node.
658: */
659: if (step->op == XML_OP_ANCESTOR)
660: xmlPatPushState(&states, i, node);
661: else
662: xmlPatPushState(&states, i - 1, node);
663: continue;
664: case XML_OP_NS:
665: if (node->type != XML_ELEMENT_NODE)
666: goto rollback;
667: if (node->ns == NULL) {
668: if (step->value != NULL)
669: goto rollback;
670: } else if (node->ns->href != NULL) {
671: if (step->value == NULL)
672: goto rollback;
673: if (!xmlStrEqual(step->value, node->ns->href))
674: goto rollback;
675: }
676: break;
677: case XML_OP_ALL:
678: if (node->type != XML_ELEMENT_NODE)
679: goto rollback;
680: break;
681: }
682: }
683: found:
684: if (states.states != NULL) {
685: /* Free the rollback states */
686: xmlFree(states.states);
687: }
688: return(1);
689: rollback:
690: /* got an error try to rollback */
691: if (states.states == NULL)
692: return(0);
693: if (states.nbstates <= 0) {
694: xmlFree(states.states);
695: return(0);
696: }
697: states.nbstates--;
698: i = states.states[states.nbstates].step;
699: node = states.states[states.nbstates].node;
700: #if 0
701: fprintf(stderr, "Pop: %d, %s\n", i, node->name);
702: #endif
703: goto restart;
704: }
705:
706: /************************************************************************
707: * *
708: * Dedicated parser for templates *
709: * *
710: ************************************************************************/
711:
1.1.1.3 ! misho 712: #define TODO \
1.1 misho 713: xmlGenericError(xmlGenericErrorContext, \
714: "Unimplemented block at %s:%d\n", \
715: __FILE__, __LINE__);
716: #define CUR (*ctxt->cur)
717: #define SKIP(val) ctxt->cur += (val)
718: #define NXT(val) ctxt->cur[(val)]
719: #define PEEKPREV(val) ctxt->cur[-(val)]
720: #define CUR_PTR ctxt->cur
721:
1.1.1.3 ! misho 722: #define SKIP_BLANKS \
1.1 misho 723: while (IS_BLANK_CH(CUR)) NEXT
724:
725: #define CURRENT (*ctxt->cur)
726: #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
727:
728:
1.1.1.3 ! misho 729: #define PUSH(op, val, val2) \
1.1 misho 730: if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
731:
732: #define XSLT_ERROR(X) \
733: { xsltError(ctxt, __FILE__, __LINE__, X); \
734: ctxt->error = (X); return; }
735:
736: #define XSLT_ERROR0(X) \
737: { xsltError(ctxt, __FILE__, __LINE__, X); \
738: ctxt->error = (X); return(0); }
739:
740: #if 0
741: /**
742: * xmlPatScanLiteral:
743: * @ctxt: the XPath Parser context
744: *
745: * Parse an XPath Litteral:
746: *
747: * [29] Literal ::= '"' [^"]* '"'
748: * | "'" [^']* "'"
749: *
750: * Returns the Literal parsed or NULL
751: */
752:
753: static xmlChar *
754: xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
755: const xmlChar *q, *cur;
756: xmlChar *ret = NULL;
757: int val, len;
758:
759: SKIP_BLANKS;
760: if (CUR == '"') {
761: NEXT;
762: cur = q = CUR_PTR;
763: val = xmlStringCurrentChar(NULL, cur, &len);
764: while ((IS_CHAR(val)) && (val != '"')) {
765: cur += len;
766: val = xmlStringCurrentChar(NULL, cur, &len);
767: }
768: if (!IS_CHAR(val)) {
769: ctxt->error = 1;
770: return(NULL);
771: } else {
772: if (ctxt->dict)
773: ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
774: else
1.1.1.3 ! misho 775: ret = xmlStrndup(q, cur - q);
1.1 misho 776: }
777: cur += len;
778: CUR_PTR = cur;
779: } else if (CUR == '\'') {
780: NEXT;
781: cur = q = CUR_PTR;
782: val = xmlStringCurrentChar(NULL, cur, &len);
783: while ((IS_CHAR(val)) && (val != '\'')) {
784: cur += len;
785: val = xmlStringCurrentChar(NULL, cur, &len);
786: }
787: if (!IS_CHAR(val)) {
788: ctxt->error = 1;
789: return(NULL);
790: } else {
791: if (ctxt->dict)
792: ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
793: else
1.1.1.3 ! misho 794: ret = xmlStrndup(q, cur - q);
1.1 misho 795: }
796: cur += len;
797: CUR_PTR = cur;
798: } else {
799: /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
800: ctxt->error = 1;
801: return(NULL);
802: }
803: return(ret);
804: }
805: #endif
806:
807: /**
808: * xmlPatScanName:
809: * @ctxt: the XPath Parser context
810: *
1.1.1.3 ! misho 811: * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
1.1 misho 812: * CombiningChar | Extender
813: *
814: * [5] Name ::= (Letter | '_' | ':') (NameChar)*
815: *
816: * [6] Names ::= Name (S Name)*
817: *
818: * Returns the Name parsed or NULL
819: */
820:
821: static xmlChar *
822: xmlPatScanName(xmlPatParserContextPtr ctxt) {
823: const xmlChar *q, *cur;
824: xmlChar *ret = NULL;
825: int val, len;
826:
827: SKIP_BLANKS;
828:
829: cur = q = CUR_PTR;
830: val = xmlStringCurrentChar(NULL, cur, &len);
831: if (!IS_LETTER(val) && (val != '_') && (val != ':'))
832: return(NULL);
833:
834: while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
835: (val == '.') || (val == '-') ||
1.1.1.3 ! misho 836: (val == '_') ||
1.1 misho 837: (IS_COMBINING(val)) ||
838: (IS_EXTENDER(val))) {
839: cur += len;
840: val = xmlStringCurrentChar(NULL, cur, &len);
841: }
842: if (ctxt->dict)
843: ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
844: else
1.1.1.3 ! misho 845: ret = xmlStrndup(q, cur - q);
1.1 misho 846: CUR_PTR = cur;
847: return(ret);
848: }
849:
850: /**
851: * xmlPatScanNCName:
852: * @ctxt: the XPath Parser context
853: *
854: * Parses a non qualified name
855: *
856: * Returns the Name parsed or NULL
857: */
858:
859: static xmlChar *
860: xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
861: const xmlChar *q, *cur;
862: xmlChar *ret = NULL;
863: int val, len;
864:
865: SKIP_BLANKS;
866:
867: cur = q = CUR_PTR;
868: val = xmlStringCurrentChar(NULL, cur, &len);
869: if (!IS_LETTER(val) && (val != '_'))
870: return(NULL);
871:
872: while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
873: (val == '.') || (val == '-') ||
874: (val == '_') ||
875: (IS_COMBINING(val)) ||
876: (IS_EXTENDER(val))) {
877: cur += len;
878: val = xmlStringCurrentChar(NULL, cur, &len);
879: }
880: if (ctxt->dict)
881: ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
882: else
883: ret = xmlStrndup(q, cur - q);
884: CUR_PTR = cur;
885: return(ret);
886: }
887:
888: #if 0
889: /**
890: * xmlPatScanQName:
891: * @ctxt: the XPath Parser context
892: * @prefix: the place to store the prefix
893: *
894: * Parse a qualified name
895: *
896: * Returns the Name parsed or NULL
897: */
898:
899: static xmlChar *
900: xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
901: xmlChar *ret = NULL;
902:
903: *prefix = NULL;
904: ret = xmlPatScanNCName(ctxt);
905: if (CUR == ':') {
906: *prefix = ret;
907: NEXT;
908: ret = xmlPatScanNCName(ctxt);
909: }
910: return(ret);
911: }
912: #endif
913:
914: /**
915: * xmlCompileAttributeTest:
916: * @ctxt: the compilation context
917: *
918: * Compile an attribute test.
919: */
920: static void
921: xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
922: xmlChar *token = NULL;
923: xmlChar *name = NULL;
924: xmlChar *URL = NULL;
1.1.1.3 ! misho 925:
1.1 misho 926: SKIP_BLANKS;
927: name = xmlPatScanNCName(ctxt);
928: if (name == NULL) {
929: if (CUR == '*') {
930: PUSH(XML_OP_ATTR, NULL, NULL);
931: NEXT;
932: } else {
933: ERROR(NULL, NULL, NULL,
934: "xmlCompileAttributeTest : Name expected\n");
935: ctxt->error = 1;
936: }
937: return;
938: }
939: if (CUR == ':') {
940: int i;
941: xmlChar *prefix = name;
1.1.1.3 ! misho 942:
1.1 misho 943: NEXT;
944:
1.1.1.3 ! misho 945: if (IS_BLANK_CH(CUR)) {
1.1 misho 946: ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
947: XML_PAT_FREE_STRING(ctxt, prefix);
948: ctxt->error = 1;
949: goto error;
950: }
951: /*
952: * This is a namespace match
953: */
954: token = xmlPatScanName(ctxt);
955: if ((prefix[0] == 'x') &&
956: (prefix[1] == 'm') &&
957: (prefix[2] == 'l') &&
958: (prefix[3] == 0))
959: {
1.1.1.3 ! misho 960: XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
1.1 misho 961: } else {
962: for (i = 0;i < ctxt->nb_namespaces;i++) {
963: if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1.1.1.3 ! misho 964: XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1.1 misho 965: break;
966: }
967: }
968: if (i >= ctxt->nb_namespaces) {
969: ERROR5(NULL, NULL, NULL,
970: "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
971: prefix);
1.1.1.3 ! misho 972: ctxt->error = 1;
1.1 misho 973: goto error;
974: }
975: }
976: XML_PAT_FREE_STRING(ctxt, prefix);
977: if (token == NULL) {
978: if (CUR == '*') {
979: NEXT;
980: PUSH(XML_OP_ATTR, NULL, URL);
981: } else {
982: ERROR(NULL, NULL, NULL,
983: "xmlCompileAttributeTest : Name expected\n");
984: ctxt->error = 1;
985: goto error;
1.1.1.3 ! misho 986: }
1.1 misho 987: } else {
988: PUSH(XML_OP_ATTR, token, URL);
989: }
990: } else {
991: PUSH(XML_OP_ATTR, name, NULL);
992: }
993: return;
994: error:
995: if (URL != NULL)
1.1.1.3 ! misho 996: XML_PAT_FREE_STRING(ctxt, URL)
1.1 misho 997: if (token != NULL)
998: XML_PAT_FREE_STRING(ctxt, token);
999: }
1000:
1001: /**
1002: * xmlCompileStepPattern:
1003: * @ctxt: the compilation context
1004: *
1005: * Compile the Step Pattern and generates a precompiled
1006: * form suitable for fast matching.
1007: *
1008: * [3] Step ::= '.' | NameTest
1.1.1.3 ! misho 1009: * [4] NameTest ::= QName | '*' | NCName ':' '*'
1.1 misho 1010: */
1011:
1012: static void
1013: xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1014: xmlChar *token = NULL;
1015: xmlChar *name = NULL;
1016: xmlChar *URL = NULL;
1017: int hasBlanks = 0;
1018:
1019: SKIP_BLANKS;
1020: if (CUR == '.') {
1021: /*
1022: * Context node.
1023: */
1024: NEXT;
1025: PUSH(XML_OP_ELEM, NULL, NULL);
1026: return;
1027: }
1028: if (CUR == '@') {
1029: /*
1030: * Attribute test.
1031: */
1032: if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1033: ERROR5(NULL, NULL, NULL,
1034: "Unexpected attribute axis in '%s'.\n", ctxt->base);
1035: ctxt->error = 1;
1036: return;
1037: }
1038: NEXT;
1039: xmlCompileAttributeTest(ctxt);
1.1.1.3 ! misho 1040: if (ctxt->error != 0)
1.1 misho 1041: goto error;
1042: return;
1043: }
1044: name = xmlPatScanNCName(ctxt);
1045: if (name == NULL) {
1046: if (CUR == '*') {
1047: NEXT;
1048: PUSH(XML_OP_ALL, NULL, NULL);
1049: return;
1050: } else {
1051: ERROR(NULL, NULL, NULL,
1052: "xmlCompileStepPattern : Name expected\n");
1053: ctxt->error = 1;
1054: return;
1055: }
1056: }
1057: if (IS_BLANK_CH(CUR)) {
1058: hasBlanks = 1;
1059: SKIP_BLANKS;
1060: }
1061: if (CUR == ':') {
1062: NEXT;
1063: if (CUR != ':') {
1064: xmlChar *prefix = name;
1.1.1.3 ! misho 1065: int i;
1.1 misho 1066:
1067: if (hasBlanks || IS_BLANK_CH(CUR)) {
1068: ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1069: ctxt->error = 1;
1070: goto error;
1071: }
1072: /*
1073: * This is a namespace match
1074: */
1075: token = xmlPatScanName(ctxt);
1076: if ((prefix[0] == 'x') &&
1077: (prefix[1] == 'm') &&
1078: (prefix[2] == 'l') &&
1079: (prefix[3] == 0))
1080: {
1081: XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1082: } else {
1083: for (i = 0;i < ctxt->nb_namespaces;i++) {
1084: if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1085: XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1086: break;
1087: }
1088: }
1089: if (i >= ctxt->nb_namespaces) {
1090: ERROR5(NULL, NULL, NULL,
1091: "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1092: prefix);
1093: ctxt->error = 1;
1094: goto error;
1095: }
1096: }
1097: XML_PAT_FREE_STRING(ctxt, prefix);
1098: name = NULL;
1099: if (token == NULL) {
1100: if (CUR == '*') {
1101: NEXT;
1102: PUSH(XML_OP_NS, URL, NULL);
1103: } else {
1104: ERROR(NULL, NULL, NULL,
1105: "xmlCompileStepPattern : Name expected\n");
1106: ctxt->error = 1;
1107: goto error;
1108: }
1109: } else {
1110: PUSH(XML_OP_ELEM, token, URL);
1111: }
1112: } else {
1113: NEXT;
1.1.1.3 ! misho 1114: if (xmlStrEqual(name, (const xmlChar *) "child")) {
1.1 misho 1115: XML_PAT_FREE_STRING(ctxt, name);
1116: name = xmlPatScanName(ctxt);
1117: if (name == NULL) {
1118: if (CUR == '*') {
1119: NEXT;
1120: PUSH(XML_OP_ALL, NULL, NULL);
1121: return;
1122: } else {
1123: ERROR(NULL, NULL, NULL,
1124: "xmlCompileStepPattern : QName expected\n");
1125: ctxt->error = 1;
1126: goto error;
1127: }
1128: }
1129: if (CUR == ':') {
1130: xmlChar *prefix = name;
1131: int i;
1.1.1.3 ! misho 1132:
1.1 misho 1133: NEXT;
1134: if (IS_BLANK_CH(CUR)) {
1135: ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1136: ctxt->error = 1;
1137: goto error;
1138: }
1139: /*
1140: * This is a namespace match
1141: */
1142: token = xmlPatScanName(ctxt);
1143: if ((prefix[0] == 'x') &&
1144: (prefix[1] == 'm') &&
1145: (prefix[2] == 'l') &&
1146: (prefix[3] == 0))
1147: {
1.1.1.3 ! misho 1148: XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1.1 misho 1149: } else {
1150: for (i = 0;i < ctxt->nb_namespaces;i++) {
1151: if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1.1.1.3 ! misho 1152: XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1.1 misho 1153: break;
1154: }
1155: }
1156: if (i >= ctxt->nb_namespaces) {
1157: ERROR5(NULL, NULL, NULL,
1158: "xmlCompileStepPattern : no namespace bound "
1159: "to prefix %s\n", prefix);
1160: ctxt->error = 1;
1161: goto error;
1162: }
1163: }
1164: XML_PAT_FREE_STRING(ctxt, prefix);
1165: name = NULL;
1166: if (token == NULL) {
1167: if (CUR == '*') {
1168: NEXT;
1169: PUSH(XML_OP_NS, URL, NULL);
1170: } else {
1171: ERROR(NULL, NULL, NULL,
1172: "xmlCompileStepPattern : Name expected\n");
1173: ctxt->error = 1;
1174: goto error;
1175: }
1176: } else {
1177: PUSH(XML_OP_CHILD, token, URL);
1178: }
1179: } else
1180: PUSH(XML_OP_CHILD, name, NULL);
1181: return;
1182: } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1183: XML_PAT_FREE_STRING(ctxt, name)
1184: name = NULL;
1185: if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1186: ERROR5(NULL, NULL, NULL,
1187: "Unexpected attribute axis in '%s'.\n", ctxt->base);
1188: ctxt->error = 1;
1189: goto error;
1190: }
1191: xmlCompileAttributeTest(ctxt);
1192: if (ctxt->error != 0)
1193: goto error;
1194: return;
1195: } else {
1196: ERROR5(NULL, NULL, NULL,
1197: "The 'element' or 'attribute' axis is expected.\n", NULL);
1198: ctxt->error = 1;
1199: goto error;
1.1.1.3 ! misho 1200: }
1.1 misho 1201: }
1202: } else if (CUR == '*') {
1203: if (name != NULL) {
1204: ctxt->error = 1;
1205: goto error;
1206: }
1207: NEXT;
1208: PUSH(XML_OP_ALL, token, NULL);
1209: } else {
1210: PUSH(XML_OP_ELEM, name, NULL);
1211: }
1212: return;
1213: error:
1214: if (URL != NULL)
1.1.1.3 ! misho 1215: XML_PAT_FREE_STRING(ctxt, URL)
1.1 misho 1216: if (token != NULL)
1217: XML_PAT_FREE_STRING(ctxt, token)
1218: if (name != NULL)
1219: XML_PAT_FREE_STRING(ctxt, name)
1220: }
1221:
1222: /**
1223: * xmlCompilePathPattern:
1224: * @ctxt: the compilation context
1225: *
1226: * Compile the Path Pattern and generates a precompiled
1227: * form suitable for fast matching.
1228: *
1.1.1.3 ! misho 1229: * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1.1 misho 1230: */
1231: static void
1232: xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1233: SKIP_BLANKS;
1234: if (CUR == '/') {
1235: ctxt->comp->flags |= PAT_FROM_ROOT;
1236: } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1237: ctxt->comp->flags |= PAT_FROM_CUR;
1238: }
1.1.1.3 ! misho 1239:
1.1 misho 1240: if ((CUR == '/') && (NXT(1) == '/')) {
1241: PUSH(XML_OP_ANCESTOR, NULL, NULL);
1242: NEXT;
1243: NEXT;
1244: } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1245: PUSH(XML_OP_ANCESTOR, NULL, NULL);
1246: NEXT;
1247: NEXT;
1248: NEXT;
1249: /* Check for incompleteness. */
1250: SKIP_BLANKS;
1251: if (CUR == 0) {
1252: ERROR5(NULL, NULL, NULL,
1253: "Incomplete expression '%s'.\n", ctxt->base);
1254: ctxt->error = 1;
1255: goto error;
1256: }
1257: }
1258: if (CUR == '@') {
1259: NEXT;
1260: xmlCompileAttributeTest(ctxt);
1261: SKIP_BLANKS;
1262: /* TODO: check for incompleteness */
1263: if (CUR != 0) {
1264: xmlCompileStepPattern(ctxt);
1265: if (ctxt->error != 0)
1266: goto error;
1267: }
1268: } else {
1269: if (CUR == '/') {
1270: PUSH(XML_OP_ROOT, NULL, NULL);
1271: NEXT;
1272: /* Check for incompleteness. */
1273: SKIP_BLANKS;
1274: if (CUR == 0) {
1275: ERROR5(NULL, NULL, NULL,
1276: "Incomplete expression '%s'.\n", ctxt->base);
1277: ctxt->error = 1;
1278: goto error;
1279: }
1280: }
1281: xmlCompileStepPattern(ctxt);
1282: if (ctxt->error != 0)
1283: goto error;
1284: SKIP_BLANKS;
1285: while (CUR == '/') {
1286: if (NXT(1) == '/') {
1287: PUSH(XML_OP_ANCESTOR, NULL, NULL);
1288: NEXT;
1289: NEXT;
1290: SKIP_BLANKS;
1291: xmlCompileStepPattern(ctxt);
1292: if (ctxt->error != 0)
1293: goto error;
1294: } else {
1295: PUSH(XML_OP_PARENT, NULL, NULL);
1296: NEXT;
1297: SKIP_BLANKS;
1298: if (CUR == 0) {
1299: ERROR5(NULL, NULL, NULL,
1300: "Incomplete expression '%s'.\n", ctxt->base);
1301: ctxt->error = 1;
1.1.1.3 ! misho 1302: goto error;
1.1 misho 1303: }
1304: xmlCompileStepPattern(ctxt);
1305: if (ctxt->error != 0)
1306: goto error;
1307: }
1308: }
1309: }
1310: if (CUR != 0) {
1311: ERROR5(NULL, NULL, NULL,
1312: "Failed to compile pattern %s\n", ctxt->base);
1313: ctxt->error = 1;
1314: }
1315: error:
1316: return;
1317: }
1318:
1319: /**
1320: * xmlCompileIDCXPathPath:
1321: * @ctxt: the compilation context
1322: *
1323: * Compile the Path Pattern and generates a precompiled
1324: * form suitable for fast matching.
1325: *
1.1.1.3 ! misho 1326: * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1.1 misho 1327: */
1328: static void
1329: xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1330: SKIP_BLANKS;
1331: if (CUR == '/') {
1332: ERROR5(NULL, NULL, NULL,
1333: "Unexpected selection of the document root in '%s'.\n",
1334: ctxt->base);
1335: goto error;
1336: }
1337: ctxt->comp->flags |= PAT_FROM_CUR;
1338:
1339: if (CUR == '.') {
1340: /* "." - "self::node()" */
1341: NEXT;
1342: SKIP_BLANKS;
1343: if (CUR == 0) {
1344: /*
1345: * Selection of the context node.
1346: */
1347: PUSH(XML_OP_ELEM, NULL, NULL);
1348: return;
1349: }
1350: if (CUR != '/') {
1351: /* TODO: A more meaningful error message. */
1352: ERROR5(NULL, NULL, NULL,
1353: "Unexpected token after '.' in '%s'.\n", ctxt->base);
1354: goto error;
1355: }
1356: /* "./" - "self::node()/" */
1357: NEXT;
1358: SKIP_BLANKS;
1359: if (CUR == '/') {
1360: if (IS_BLANK_CH(PEEKPREV(1))) {
1361: /*
1362: * Disallow "./ /"
1363: */
1364: ERROR5(NULL, NULL, NULL,
1365: "Unexpected '/' token in '%s'.\n", ctxt->base);
1366: goto error;
1367: }
1368: /* ".//" - "self:node()/descendant-or-self::node()/" */
1369: PUSH(XML_OP_ANCESTOR, NULL, NULL);
1370: NEXT;
1371: SKIP_BLANKS;
1372: }
1373: if (CUR == 0)
1374: goto error_unfinished;
1375: }
1376: /*
1377: * Process steps.
1378: */
1379: do {
1380: xmlCompileStepPattern(ctxt);
1.1.1.3 ! misho 1381: if (ctxt->error != 0)
1.1 misho 1382: goto error;
1383: SKIP_BLANKS;
1384: if (CUR != '/')
1385: break;
1386: PUSH(XML_OP_PARENT, NULL, NULL);
1387: NEXT;
1388: SKIP_BLANKS;
1389: if (CUR == '/') {
1390: /*
1391: * Disallow subsequent '//'.
1392: */
1393: ERROR5(NULL, NULL, NULL,
1394: "Unexpected subsequent '//' in '%s'.\n",
1395: ctxt->base);
1396: goto error;
1397: }
1398: if (CUR == 0)
1399: goto error_unfinished;
1.1.1.3 ! misho 1400:
1.1 misho 1401: } while (CUR != 0);
1402:
1403: if (CUR != 0) {
1404: ERROR5(NULL, NULL, NULL,
1405: "Failed to compile expression '%s'.\n", ctxt->base);
1406: ctxt->error = 1;
1407: }
1408: return;
1409: error:
1410: ctxt->error = 1;
1411: return;
1412:
1413: error_unfinished:
1414: ctxt->error = 1;
1415: ERROR5(NULL, NULL, NULL,
1.1.1.3 ! misho 1416: "Unfinished expression '%s'.\n", ctxt->base);
1.1 misho 1417: return;
1418: }
1419:
1420: /************************************************************************
1421: * *
1422: * The streaming code *
1423: * *
1424: ************************************************************************/
1425:
1426: #ifdef DEBUG_STREAMING
1427: static void
1428: xmlDebugStreamComp(xmlStreamCompPtr stream) {
1429: int i;
1430:
1431: if (stream == NULL) {
1432: printf("Stream: NULL\n");
1433: return;
1434: }
1435: printf("Stream: %d steps\n", stream->nbStep);
1436: for (i = 0;i < stream->nbStep;i++) {
1437: if (stream->steps[i].ns != NULL) {
1438: printf("{%s}", stream->steps[i].ns);
1439: }
1440: if (stream->steps[i].name == NULL) {
1441: printf("* ");
1442: } else {
1443: printf("%s ", stream->steps[i].name);
1444: }
1445: if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1446: printf("root ");
1447: if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1448: printf("// ");
1449: if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1450: printf("final ");
1451: printf("\n");
1452: }
1453: }
1454: static void
1455: xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1456: int i;
1457:
1458: if (ctxt == NULL) {
1459: printf("Stream: NULL\n");
1460: return;
1461: }
1462: printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1463: if (match)
1464: printf("matches\n");
1465: else
1466: printf("\n");
1467: for (i = 0;i < ctxt->nbState;i++) {
1468: if (ctxt->states[2 * i] < 0)
1469: printf(" %d: free\n", i);
1470: else {
1471: printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1472: ctxt->states[(2 * i) + 1]);
1473: if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1474: XML_STREAM_STEP_DESC)
1475: printf(" //\n");
1476: else
1477: printf("\n");
1478: }
1479: }
1480: }
1481: #endif
1482: /**
1483: * xmlNewStreamComp:
1484: * @size: the number of expected steps
1485: *
1486: * build a new compiled pattern for streaming
1487: *
1488: * Returns the new structure or NULL in case of error.
1489: */
1490: static xmlStreamCompPtr
1491: xmlNewStreamComp(int size) {
1492: xmlStreamCompPtr cur;
1493:
1494: if (size < 4)
1495: size = 4;
1496:
1497: cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1498: if (cur == NULL) {
1499: ERROR(NULL, NULL, NULL,
1500: "xmlNewStreamComp: malloc failed\n");
1501: return(NULL);
1502: }
1503: memset(cur, 0, sizeof(xmlStreamComp));
1504: cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1505: if (cur->steps == NULL) {
1506: xmlFree(cur);
1507: ERROR(NULL, NULL, NULL,
1508: "xmlNewStreamComp: malloc failed\n");
1509: return(NULL);
1510: }
1511: cur->nbStep = 0;
1512: cur->maxStep = size;
1513: return(cur);
1514: }
1515:
1516: /**
1517: * xmlFreeStreamComp:
1518: * @comp: the compiled pattern for streaming
1519: *
1520: * Free the compiled pattern for streaming
1521: */
1522: static void
1523: xmlFreeStreamComp(xmlStreamCompPtr comp) {
1524: if (comp != NULL) {
1525: if (comp->steps != NULL)
1526: xmlFree(comp->steps);
1527: if (comp->dict != NULL)
1528: xmlDictFree(comp->dict);
1529: xmlFree(comp);
1530: }
1531: }
1532:
1533: /**
1534: * xmlStreamCompAddStep:
1535: * @comp: the compiled pattern for streaming
1536: * @name: the first string, the name, or NULL for *
1537: * @ns: the second step, the namespace name
1538: * @flags: the flags for that step
1539: *
1540: * Add a new step to the compiled pattern
1541: *
1542: * Returns -1 in case of error or the step index if successful
1543: */
1544: static int
1545: xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1546: const xmlChar *ns, int nodeType, int flags) {
1547: xmlStreamStepPtr cur;
1548:
1549: if (comp->nbStep >= comp->maxStep) {
1550: cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1551: comp->maxStep * 2 * sizeof(xmlStreamStep));
1552: if (cur == NULL) {
1553: ERROR(NULL, NULL, NULL,
1554: "xmlNewStreamComp: malloc failed\n");
1555: return(-1);
1556: }
1557: comp->steps = cur;
1558: comp->maxStep *= 2;
1559: }
1560: cur = &comp->steps[comp->nbStep++];
1561: cur->flags = flags;
1562: cur->name = name;
1563: cur->ns = ns;
1564: cur->nodeType = nodeType;
1565: return(comp->nbStep - 1);
1566: }
1567:
1568: /**
1569: * xmlStreamCompile:
1570: * @comp: the precompiled pattern
1.1.1.3 ! misho 1571: *
1.1 misho 1572: * Tries to stream compile a pattern
1573: *
1574: * Returns -1 in case of failure and 0 in case of success.
1575: */
1576: static int
1577: xmlStreamCompile(xmlPatternPtr comp) {
1578: xmlStreamCompPtr stream;
1579: int i, s = 0, root = 0, flags = 0, prevs = -1;
1580: xmlStepOp step;
1581:
1582: if ((comp == NULL) || (comp->steps == NULL))
1583: return(-1);
1584: /*
1585: * special case for .
1586: */
1587: if ((comp->nbStep == 1) &&
1588: (comp->steps[0].op == XML_OP_ELEM) &&
1589: (comp->steps[0].value == NULL) &&
1590: (comp->steps[0].value2 == NULL)) {
1591: stream = xmlNewStreamComp(0);
1592: if (stream == NULL)
1593: return(-1);
1594: /* Note that the stream will have no steps in this case. */
1595: stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1596: comp->stream = stream;
1597: return(0);
1598: }
1599:
1600: stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1601: if (stream == NULL)
1602: return(-1);
1603: if (comp->dict != NULL) {
1604: stream->dict = comp->dict;
1605: xmlDictReference(stream->dict);
1606: }
1607:
1.1.1.3 ! misho 1608: i = 0;
1.1 misho 1609: if (comp->flags & PAT_FROM_ROOT)
1610: stream->flags |= XML_STREAM_FROM_ROOT;
1611:
1612: for (;i < comp->nbStep;i++) {
1613: step = comp->steps[i];
1614: switch (step.op) {
1615: case XML_OP_END:
1616: break;
1617: case XML_OP_ROOT:
1618: if (i != 0)
1619: goto error;
1620: root = 1;
1621: break;
1622: case XML_OP_NS:
1623: s = xmlStreamCompAddStep(stream, NULL, step.value,
1.1.1.3 ! misho 1624: XML_ELEMENT_NODE, flags);
1.1 misho 1625: if (s < 0)
1626: goto error;
1627: prevs = s;
1.1.1.3 ! misho 1628: flags = 0;
! 1629: break;
1.1 misho 1630: case XML_OP_ATTR:
1631: flags |= XML_STREAM_STEP_ATTR;
1632: prevs = -1;
1633: s = xmlStreamCompAddStep(stream,
1634: step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1635: flags = 0;
1636: if (s < 0)
1637: goto error;
1638: break;
1.1.1.3 ! misho 1639: case XML_OP_ELEM:
1.1 misho 1640: if ((step.value == NULL) && (step.value2 == NULL)) {
1641: /*
1642: * We have a "." or "self::node()" here.
1643: * Eliminate redundant self::node() tests like in "/./."
1644: * or "//./"
1645: * The only case we won't eliminate is "//.", i.e. if
1646: * self::node() is the last node test and we had
1647: * continuation somewhere beforehand.
1648: */
1649: if ((comp->nbStep == i + 1) &&
1650: (flags & XML_STREAM_STEP_DESC)) {
1651: /*
1652: * Mark the special case where the expression resolves
1653: * to any type of node.
1654: */
1655: if (comp->nbStep == i + 1) {
1656: stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1657: }
1.1.1.3 ! misho 1658: flags |= XML_STREAM_STEP_NODE;
1.1 misho 1659: s = xmlStreamCompAddStep(stream, NULL, NULL,
1660: XML_STREAM_ANY_NODE, flags);
1661: if (s < 0)
1662: goto error;
1663: flags = 0;
1664: /*
1665: * If there was a previous step, mark it to be added to
1666: * the result node-set; this is needed since only
1667: * the last step will be marked as "final" and only
1668: * "final" nodes are added to the resulting set.
1669: */
1670: if (prevs != -1) {
1671: stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1672: prevs = -1;
1673: }
1.1.1.3 ! misho 1674: break;
1.1 misho 1675:
1676: } else {
1677: /* Just skip this one. */
1678: continue;
1679: }
1680: }
1.1.1.3 ! misho 1681: /* An element node. */
1.1 misho 1682: s = xmlStreamCompAddStep(stream, step.value, step.value2,
1.1.1.3 ! misho 1683: XML_ELEMENT_NODE, flags);
1.1 misho 1684: if (s < 0)
1685: goto error;
1686: prevs = s;
1.1.1.3 ! misho 1687: flags = 0;
! 1688: break;
1.1 misho 1689: case XML_OP_CHILD:
1690: /* An element node child. */
1691: s = xmlStreamCompAddStep(stream, step.value, step.value2,
1.1.1.3 ! misho 1692: XML_ELEMENT_NODE, flags);
1.1 misho 1693: if (s < 0)
1694: goto error;
1695: prevs = s;
1696: flags = 0;
1.1.1.3 ! misho 1697: break;
1.1 misho 1698: case XML_OP_ALL:
1699: s = xmlStreamCompAddStep(stream, NULL, NULL,
1.1.1.3 ! misho 1700: XML_ELEMENT_NODE, flags);
1.1 misho 1701: if (s < 0)
1702: goto error;
1703: prevs = s;
1704: flags = 0;
1705: break;
1.1.1.3 ! misho 1706: case XML_OP_PARENT:
1.1 misho 1707: break;
1708: case XML_OP_ANCESTOR:
1709: /* Skip redundant continuations. */
1710: if (flags & XML_STREAM_STEP_DESC)
1711: break;
1712: flags |= XML_STREAM_STEP_DESC;
1713: /*
1714: * Mark the expression as having "//".
1715: */
1716: if ((stream->flags & XML_STREAM_DESC) == 0)
1717: stream->flags |= XML_STREAM_DESC;
1718: break;
1719: }
1.1.1.3 ! misho 1720: }
1.1 misho 1721: if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1722: /*
1723: * If this should behave like a real pattern, we will mark
1724: * the first step as having "//", to be reentrant on every
1725: * tree level.
1726: */
1727: if ((stream->flags & XML_STREAM_DESC) == 0)
1728: stream->flags |= XML_STREAM_DESC;
1729:
1730: if (stream->nbStep > 0) {
1731: if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1.1.1.3 ! misho 1732: stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1.1 misho 1733: }
1734: }
1735: if (stream->nbStep <= s)
1736: goto error;
1737: stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1738: if (root)
1739: stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1740: #ifdef DEBUG_STREAMING
1741: xmlDebugStreamComp(stream);
1742: #endif
1743: comp->stream = stream;
1744: return(0);
1745: error:
1746: xmlFreeStreamComp(stream);
1747: return(0);
1748: }
1749:
1750: /**
1751: * xmlNewStreamCtxt:
1752: * @size: the number of expected states
1753: *
1754: * build a new stream context
1755: *
1756: * Returns the new structure or NULL in case of error.
1757: */
1758: static xmlStreamCtxtPtr
1759: xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1760: xmlStreamCtxtPtr cur;
1761:
1762: cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1763: if (cur == NULL) {
1764: ERROR(NULL, NULL, NULL,
1765: "xmlNewStreamCtxt: malloc failed\n");
1766: return(NULL);
1767: }
1768: memset(cur, 0, sizeof(xmlStreamCtxt));
1769: cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1770: if (cur->states == NULL) {
1771: xmlFree(cur);
1772: ERROR(NULL, NULL, NULL,
1773: "xmlNewStreamCtxt: malloc failed\n");
1774: return(NULL);
1775: }
1776: cur->nbState = 0;
1777: cur->maxState = 4;
1778: cur->level = 0;
1779: cur->comp = stream;
1780: cur->blockLevel = -1;
1781: return(cur);
1782: }
1783:
1784: /**
1785: * xmlFreeStreamCtxt:
1786: * @stream: the stream context
1787: *
1788: * Free the stream context
1789: */
1790: void
1791: xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1792: xmlStreamCtxtPtr next;
1793:
1794: while (stream != NULL) {
1795: next = stream->next;
1796: if (stream->states != NULL)
1797: xmlFree(stream->states);
1798: xmlFree(stream);
1799: stream = next;
1800: }
1801: }
1802:
1803: /**
1804: * xmlStreamCtxtAddState:
1805: * @comp: the stream context
1806: * @idx: the step index for that streaming state
1807: *
1808: * Add a new state to the stream context
1809: *
1810: * Returns -1 in case of error or the state index if successful
1811: */
1812: static int
1813: xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1814: int i;
1815: for (i = 0;i < comp->nbState;i++) {
1816: if (comp->states[2 * i] < 0) {
1817: comp->states[2 * i] = idx;
1818: comp->states[2 * i + 1] = level;
1819: return(i);
1820: }
1821: }
1822: if (comp->nbState >= comp->maxState) {
1823: int *cur;
1824:
1825: cur = (int *) xmlRealloc(comp->states,
1826: comp->maxState * 4 * sizeof(int));
1827: if (cur == NULL) {
1828: ERROR(NULL, NULL, NULL,
1829: "xmlNewStreamCtxt: malloc failed\n");
1830: return(-1);
1831: }
1832: comp->states = cur;
1833: comp->maxState *= 2;
1834: }
1835: comp->states[2 * comp->nbState] = idx;
1836: comp->states[2 * comp->nbState++ + 1] = level;
1837: return(comp->nbState - 1);
1838: }
1839:
1840: /**
1841: * xmlStreamPushInternal:
1842: * @stream: the stream context
1843: * @name: the current name
1844: * @ns: the namespace name
1845: * @nodeType: the type of the node
1846: *
1847: * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1848: * indicated a dictionary, then strings for name and ns will be expected
1849: * to come from the dictionary.
1850: * Both @name and @ns being NULL means the / i.e. the root of the document.
1851: * This can also act as a reset.
1852: *
1853: * Returns: -1 in case of error, 1 if the current state in the stream is a
1854: * match and 0 otherwise.
1855: */
1856: static int
1857: xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1858: const xmlChar *name, const xmlChar *ns,
1859: int nodeType) {
1860: int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1861: xmlStreamCompPtr comp;
1862: xmlStreamStep step;
1863: #ifdef DEBUG_STREAMING
1864: xmlStreamCtxtPtr orig = stream;
1865: #endif
1866:
1867: if ((stream == NULL) || (stream->nbState < 0))
1868: return(-1);
1869:
1870: while (stream != NULL) {
1871: comp = stream->comp;
1872:
1873: if ((nodeType == XML_ELEMENT_NODE) &&
1874: (name == NULL) && (ns == NULL)) {
1875: /* We have a document node here (or a reset). */
1876: stream->nbState = 0;
1877: stream->level = 0;
1878: stream->blockLevel = -1;
1879: if (comp->flags & XML_STREAM_FROM_ROOT) {
1880: if (comp->nbStep == 0) {
1881: /* TODO: We have a "/." here? */
1882: ret = 1;
1883: } else {
1884: if ((comp->nbStep == 1) &&
1885: (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1886: (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1887: {
1888: /*
1889: * In the case of "//." the document node will match
1890: * as well.
1891: */
1892: ret = 1;
1893: } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1894: /* TODO: Do we need this ? */
1895: tmp = xmlStreamCtxtAddState(stream, 0, 0);
1896: if (tmp < 0)
1897: err++;
1898: }
1899: }
1900: }
1901: stream = stream->next;
1902: continue; /* while */
1903: }
1904:
1905: /*
1906: * Fast check for ".".
1907: */
1908: if (comp->nbStep == 0) {
1909: /*
1910: * / and . are handled at the XPath node set creation
1911: * level by checking min depth
1912: */
1913: if (stream->flags & XML_PATTERN_XPATH) {
1914: stream = stream->next;
1915: continue; /* while */
1916: }
1917: /*
1918: * For non-pattern like evaluation like XML Schema IDCs
1919: * or traditional XPath expressions, this will match if
1920: * we are at the first level only, otherwise on every level.
1921: */
1922: if ((nodeType != XML_ATTRIBUTE_NODE) &&
1923: (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1924: (stream->level == 0))) {
1.1.1.3 ! misho 1925: ret = 1;
1.1 misho 1926: }
1927: stream->level++;
1928: goto stream_next;
1929: }
1930: if (stream->blockLevel != -1) {
1931: /*
1932: * Skip blocked expressions.
1933: */
1.1.1.3 ! misho 1934: stream->level++;
1.1 misho 1935: goto stream_next;
1936: }
1937:
1938: if ((nodeType != XML_ELEMENT_NODE) &&
1939: (nodeType != XML_ATTRIBUTE_NODE) &&
1940: ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1941: /*
1942: * No need to process nodes of other types if we don't
1943: * resolve to those types.
1944: * TODO: Do we need to block the context here?
1945: */
1946: stream->level++;
1947: goto stream_next;
1948: }
1949:
1950: /*
1951: * Check evolution of existing states
1952: */
1953: i = 0;
1954: m = stream->nbState;
1955: while (i < m) {
1956: if ((comp->flags & XML_STREAM_DESC) == 0) {
1957: /*
1958: * If there is no "//", then only the last
1959: * added state is of interest.
1960: */
1961: stepNr = stream->states[2 * (stream->nbState -1)];
1962: /*
1963: * TODO: Security check, should not happen, remove it.
1964: */
1965: if (stream->states[(2 * (stream->nbState -1)) + 1] <
1966: stream->level) {
1967: return (-1);
1968: }
1969: desc = 0;
1970: /* loop-stopper */
1971: i = m;
1972: } else {
1973: /*
1974: * If there are "//", then we need to process every "//"
1975: * occuring in the states, plus any other state for this
1976: * level.
1.1.1.3 ! misho 1977: */
1.1 misho 1978: stepNr = stream->states[2 * i];
1979:
1980: /* TODO: should not happen anymore: dead states */
1981: if (stepNr < 0)
1982: goto next_state;
1983:
1984: tmp = stream->states[(2 * i) + 1];
1985:
1986: /* skip new states just added */
1987: if (tmp > stream->level)
1988: goto next_state;
1989:
1990: /* skip states at ancestor levels, except if "//" */
1991: desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1992: if ((tmp < stream->level) && (!desc))
1993: goto next_state;
1994: }
1.1.1.3 ! misho 1995: /*
1.1 misho 1996: * Check for correct node-type.
1997: */
1998: step = comp->steps[stepNr];
1999: if (step.nodeType != nodeType) {
2000: if (step.nodeType == XML_ATTRIBUTE_NODE) {
2001: /*
2002: * Block this expression for deeper evaluation.
2003: */
2004: if ((comp->flags & XML_STREAM_DESC) == 0)
2005: stream->blockLevel = stream->level +1;
2006: goto next_state;
2007: } else if (step.nodeType != XML_STREAM_ANY_NODE)
2008: goto next_state;
1.1.1.3 ! misho 2009: }
1.1 misho 2010: /*
2011: * Compare local/namespace-name.
2012: */
2013: match = 0;
2014: if (step.nodeType == XML_STREAM_ANY_NODE) {
2015: match = 1;
2016: } else if (step.name == NULL) {
2017: if (step.ns == NULL) {
2018: /*
2019: * This lets through all elements/attributes.
2020: */
2021: match = 1;
2022: } else if (ns != NULL)
2023: match = xmlStrEqual(step.ns, ns);
2024: } else if (((step.ns != NULL) == (ns != NULL)) &&
2025: (name != NULL) &&
2026: (step.name[0] == name[0]) &&
2027: xmlStrEqual(step.name, name) &&
2028: ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2029: {
1.1.1.3 ! misho 2030: match = 1;
! 2031: }
! 2032: #if 0
1.1 misho 2033: /*
2034: * TODO: Pointer comparison won't work, since not guaranteed that the given
2035: * values are in the same dict; especially if it's the namespace name,
2036: * normally coming from ns->href. We need a namespace dict mechanism !
2037: */
2038: } else if (comp->dict) {
2039: if (step.name == NULL) {
2040: if (step.ns == NULL)
2041: match = 1;
2042: else
2043: match = (step.ns == ns);
2044: } else {
2045: match = ((step.name == name) && (step.ns == ns));
2046: }
1.1.1.3 ! misho 2047: #endif /* if 0 ------------------------------------------------------- */
! 2048: if (match) {
1.1 misho 2049: final = step.flags & XML_STREAM_STEP_FINAL;
2050: if (desc) {
2051: if (final) {
2052: ret = 1;
2053: } else {
2054: /* descending match create a new state */
2055: xmlStreamCtxtAddState(stream, stepNr + 1,
2056: stream->level + 1);
2057: }
2058: } else {
2059: if (final) {
2060: ret = 1;
2061: } else {
2062: xmlStreamCtxtAddState(stream, stepNr + 1,
2063: stream->level + 1);
2064: }
2065: }
2066: if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2067: /*
2068: * Check if we have a special case like "foo/bar//.", where
2069: * "foo" is selected as well.
2070: */
2071: ret = 1;
2072: }
1.1.1.3 ! misho 2073: }
1.1 misho 2074: if (((comp->flags & XML_STREAM_DESC) == 0) &&
2075: ((! match) || final)) {
2076: /*
2077: * Mark this expression as blocked for any evaluation at
2078: * deeper levels. Note that this includes "/foo"
2079: * expressions if the *pattern* behaviour is used.
2080: */
2081: stream->blockLevel = stream->level +1;
2082: }
2083: next_state:
2084: i++;
2085: }
2086:
2087: stream->level++;
2088:
2089: /*
2090: * Re/enter the expression.
2091: * Don't reenter if it's an absolute expression like "/foo",
2092: * except "//foo".
2093: */
2094: step = comp->steps[0];
2095: if (step.flags & XML_STREAM_STEP_ROOT)
2096: goto stream_next;
2097:
2098: desc = step.flags & XML_STREAM_STEP_DESC;
2099: if (stream->flags & XML_PATTERN_NOTPATTERN) {
2100: /*
2101: * Re/enter the expression if it is a "descendant" one,
2102: * or if we are at the 1st level of evaluation.
2103: */
1.1.1.3 ! misho 2104:
1.1 misho 2105: if (stream->level == 1) {
2106: if (XML_STREAM_XS_IDC(stream)) {
2107: /*
2108: * XS-IDC: The missing "self::node()" will always
2109: * match the first given node.
2110: */
2111: goto stream_next;
2112: } else
2113: goto compare;
1.1.1.3 ! misho 2114: }
1.1 misho 2115: /*
2116: * A "//" is always reentrant.
2117: */
2118: if (desc)
2119: goto compare;
2120:
2121: /*
2122: * XS-IDC: Process the 2nd level, since the missing
2123: * "self::node()" is responsible for the 2nd level being
1.1.1.3 ! misho 2124: * the real start level.
! 2125: */
1.1 misho 2126: if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2127: goto compare;
2128:
2129: goto stream_next;
2130: }
1.1.1.3 ! misho 2131:
1.1 misho 2132: compare:
2133: /*
2134: * Check expected node-type.
2135: */
2136: if (step.nodeType != nodeType) {
2137: if (nodeType == XML_ATTRIBUTE_NODE)
2138: goto stream_next;
2139: else if (step.nodeType != XML_STREAM_ANY_NODE)
1.1.1.3 ! misho 2140: goto stream_next;
1.1 misho 2141: }
2142: /*
2143: * Compare local/namespace-name.
2144: */
2145: match = 0;
2146: if (step.nodeType == XML_STREAM_ANY_NODE) {
2147: match = 1;
2148: } else if (step.name == NULL) {
2149: if (step.ns == NULL) {
2150: /*
2151: * This lets through all elements/attributes.
2152: */
2153: match = 1;
2154: } else if (ns != NULL)
2155: match = xmlStrEqual(step.ns, ns);
2156: } else if (((step.ns != NULL) == (ns != NULL)) &&
2157: (name != NULL) &&
2158: (step.name[0] == name[0]) &&
2159: xmlStrEqual(step.name, name) &&
2160: ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2161: {
1.1.1.3 ! misho 2162: match = 1;
! 2163: }
1.1 misho 2164: final = step.flags & XML_STREAM_STEP_FINAL;
1.1.1.3 ! misho 2165: if (match) {
1.1 misho 2166: if (final)
2167: ret = 1;
2168: else
2169: xmlStreamCtxtAddState(stream, 1, stream->level);
2170: if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2171: /*
2172: * Check if we have a special case like "foo//.", where
2173: * "foo" is selected as well.
2174: */
2175: ret = 1;
2176: }
2177: }
2178: if (((comp->flags & XML_STREAM_DESC) == 0) &&
2179: ((! match) || final)) {
2180: /*
2181: * Mark this expression as blocked for any evaluation at
2182: * deeper levels.
2183: */
2184: stream->blockLevel = stream->level;
2185: }
2186:
2187: stream_next:
2188: stream = stream->next;
2189: } /* while stream != NULL */
1.1.1.3 ! misho 2190:
1.1 misho 2191: if (err > 0)
2192: ret = -1;
2193: #ifdef DEBUG_STREAMING
2194: xmlDebugStreamCtxt(orig, ret);
2195: #endif
2196: return(ret);
2197: }
2198:
2199: /**
2200: * xmlStreamPush:
2201: * @stream: the stream context
2202: * @name: the current name
2203: * @ns: the namespace name
2204: *
2205: * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2206: * indicated a dictionary, then strings for name and ns will be expected
2207: * to come from the dictionary.
2208: * Both @name and @ns being NULL means the / i.e. the root of the document.
2209: * This can also act as a reset.
2210: * Otherwise the function will act as if it has been given an element-node.
2211: *
2212: * Returns: -1 in case of error, 1 if the current state in the stream is a
2213: * match and 0 otherwise.
2214: */
2215: int
2216: xmlStreamPush(xmlStreamCtxtPtr stream,
2217: const xmlChar *name, const xmlChar *ns) {
2218: return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2219: }
2220:
2221: /**
2222: * xmlStreamPushNode:
2223: * @stream: the stream context
2224: * @name: the current name
2225: * @ns: the namespace name
2226: * @nodeType: the type of the node being pushed
2227: *
2228: * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2229: * indicated a dictionary, then strings for name and ns will be expected
2230: * to come from the dictionary.
2231: * Both @name and @ns being NULL means the / i.e. the root of the document.
2232: * This can also act as a reset.
2233: * Different from xmlStreamPush() this function can be fed with nodes of type:
2234: * element-, attribute-, text-, cdata-section-, comment- and
2235: * processing-instruction-node.
2236: *
2237: * Returns: -1 in case of error, 1 if the current state in the stream is a
2238: * match and 0 otherwise.
2239: */
2240: int
2241: xmlStreamPushNode(xmlStreamCtxtPtr stream,
2242: const xmlChar *name, const xmlChar *ns,
2243: int nodeType)
2244: {
2245: return (xmlStreamPushInternal(stream, name, ns,
2246: nodeType));
2247: }
2248:
2249: /**
2250: * xmlStreamPushAttr:
2251: * @stream: the stream context
2252: * @name: the current name
2253: * @ns: the namespace name
2254: *
2255: * Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2256: * indicated a dictionary, then strings for name and ns will be expected
2257: * to come from the dictionary.
2258: * Both @name and @ns being NULL means the / i.e. the root of the document.
2259: * This can also act as a reset.
2260: * Otherwise the function will act as if it has been given an attribute-node.
2261: *
2262: * Returns: -1 in case of error, 1 if the current state in the stream is a
2263: * match and 0 otherwise.
2264: */
2265: int
2266: xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2267: const xmlChar *name, const xmlChar *ns) {
2268: return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
2269: }
2270:
2271: /**
2272: * xmlStreamPop:
2273: * @stream: the stream context
2274: *
2275: * push one level from the stream.
2276: *
2277: * Returns: -1 in case of error, 0 otherwise.
2278: */
2279: int
2280: xmlStreamPop(xmlStreamCtxtPtr stream) {
2281: int i, lev;
1.1.1.3 ! misho 2282:
1.1 misho 2283: if (stream == NULL)
2284: return(-1);
2285: while (stream != NULL) {
2286: /*
2287: * Reset block-level.
2288: */
2289: if (stream->blockLevel == stream->level)
2290: stream->blockLevel = -1;
2291:
2292: /*
2293: * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2294: * (see the thread at
2295: * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2296: */
2297: if (stream->level)
2298: stream->level--;
2299: /*
2300: * Check evolution of existing states
1.1.1.3 ! misho 2301: */
1.1 misho 2302: for (i = stream->nbState -1; i >= 0; i--) {
2303: /* discard obsoleted states */
2304: lev = stream->states[(2 * i) + 1];
2305: if (lev > stream->level)
2306: stream->nbState--;
2307: if (lev <= stream->level)
2308: break;
2309: }
2310: stream = stream->next;
2311: }
2312: return(0);
2313: }
2314:
2315: /**
2316: * xmlStreamWantsAnyNode:
2317: * @streamCtxt: the stream context
2318: *
2319: * Query if the streaming pattern additionally needs to be fed with
2320: * text-, cdata-section-, comment- and processing-instruction-nodes.
2321: * If the result is 0 then only element-nodes and attribute-nodes
2322: * need to be pushed.
2323: *
2324: * Returns: 1 in case of need of nodes of the above described types,
2325: * 0 otherwise. -1 on API errors.
2326: */
2327: int
2328: xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
1.1.1.3 ! misho 2329: {
1.1 misho 2330: if (streamCtxt == NULL)
2331: return(-1);
2332: while (streamCtxt != NULL) {
1.1.1.3 ! misho 2333: if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
1.1 misho 2334: return(1);
2335: streamCtxt = streamCtxt->next;
2336: }
2337: return(0);
2338: }
2339:
2340: /************************************************************************
2341: * *
2342: * The public interfaces *
2343: * *
2344: ************************************************************************/
2345:
2346: /**
2347: * xmlPatterncompile:
2348: * @pattern: the pattern to compile
2349: * @dict: an optional dictionary for interned strings
2350: * @flags: compilation flags, see xmlPatternFlags
2351: * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2352: *
2353: * Compile a pattern.
2354: *
2355: * Returns the compiled form of the pattern or NULL in case of error
2356: */
2357: xmlPatternPtr
2358: xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2359: const xmlChar **namespaces) {
2360: xmlPatternPtr ret = NULL, cur;
2361: xmlPatParserContextPtr ctxt = NULL;
2362: const xmlChar *or, *start;
2363: xmlChar *tmp = NULL;
2364: int type = 0;
2365: int streamable = 1;
2366:
2367: if (pattern == NULL)
2368: return(NULL);
2369:
2370: start = pattern;
2371: or = start;
2372: while (*or != 0) {
2373: tmp = NULL;
2374: while ((*or != 0) && (*or != '|')) or++;
2375: if (*or == 0)
2376: ctxt = xmlNewPatParserContext(start, dict, namespaces);
2377: else {
2378: tmp = xmlStrndup(start, or - start);
2379: if (tmp != NULL) {
2380: ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2381: }
2382: or++;
2383: }
1.1.1.3 ! misho 2384: if (ctxt == NULL) goto error;
1.1 misho 2385: cur = xmlNewPattern();
2386: if (cur == NULL) goto error;
2387: /*
2388: * Assign string dict.
2389: */
1.1.1.3 ! misho 2390: if (dict) {
1.1 misho 2391: cur->dict = dict;
2392: xmlDictReference(dict);
2393: }
2394: if (ret == NULL)
2395: ret = cur;
2396: else {
2397: cur->next = ret->next;
2398: ret->next = cur;
2399: }
2400: cur->flags = flags;
2401: ctxt->comp = cur;
2402:
2403: if (XML_STREAM_XS_IDC(cur))
2404: xmlCompileIDCXPathPath(ctxt);
2405: else
2406: xmlCompilePathPattern(ctxt);
2407: if (ctxt->error != 0)
2408: goto error;
2409: xmlFreePatParserContext(ctxt);
2410: ctxt = NULL;
2411:
2412:
2413: if (streamable) {
2414: if (type == 0) {
2415: type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2416: } else if (type == PAT_FROM_ROOT) {
2417: if (cur->flags & PAT_FROM_CUR)
2418: streamable = 0;
2419: } else if (type == PAT_FROM_CUR) {
2420: if (cur->flags & PAT_FROM_ROOT)
2421: streamable = 0;
2422: }
2423: }
2424: if (streamable)
2425: xmlStreamCompile(cur);
2426: if (xmlReversePattern(cur) < 0)
2427: goto error;
2428: if (tmp != NULL) {
2429: xmlFree(tmp);
2430: tmp = NULL;
2431: }
2432: start = or;
2433: }
2434: if (streamable == 0) {
2435: cur = ret;
2436: while (cur != NULL) {
2437: if (cur->stream != NULL) {
2438: xmlFreeStreamComp(cur->stream);
2439: cur->stream = NULL;
2440: }
2441: cur = cur->next;
2442: }
2443: }
2444:
2445: return(ret);
2446: error:
2447: if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2448: if (ret != NULL) xmlFreePattern(ret);
2449: if (tmp != NULL) xmlFree(tmp);
2450: return(NULL);
2451: }
2452:
2453: /**
2454: * xmlPatternMatch:
2455: * @comp: the precompiled pattern
2456: * @node: a node
2457: *
2458: * Test whether the node matches the pattern
2459: *
2460: * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2461: */
2462: int
2463: xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2464: {
2465: int ret = 0;
2466:
2467: if ((comp == NULL) || (node == NULL))
2468: return(-1);
2469:
2470: while (comp != NULL) {
2471: ret = xmlPatMatch(comp, node);
2472: if (ret != 0)
2473: return(ret);
2474: comp = comp->next;
2475: }
2476: return(ret);
2477: }
2478:
2479: /**
2480: * xmlPatternGetStreamCtxt:
2481: * @comp: the precompiled pattern
2482: *
2483: * Get a streaming context for that pattern
2484: * Use xmlFreeStreamCtxt to free the context.
2485: *
2486: * Returns a pointer to the context or NULL in case of failure
2487: */
2488: xmlStreamCtxtPtr
2489: xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2490: {
2491: xmlStreamCtxtPtr ret = NULL, cur;
2492:
2493: if ((comp == NULL) || (comp->stream == NULL))
2494: return(NULL);
2495:
2496: while (comp != NULL) {
2497: if (comp->stream == NULL)
2498: goto failed;
2499: cur = xmlNewStreamCtxt(comp->stream);
2500: if (cur == NULL)
2501: goto failed;
2502: if (ret == NULL)
2503: ret = cur;
2504: else {
2505: cur->next = ret->next;
2506: ret->next = cur;
2507: }
2508: cur->flags = comp->flags;
2509: comp = comp->next;
2510: }
2511: return(ret);
2512: failed:
2513: xmlFreeStreamCtxt(ret);
2514: return(NULL);
2515: }
2516:
2517: /**
2518: * xmlPatternStreamable:
2519: * @comp: the precompiled pattern
2520: *
2521: * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2522: * should work.
2523: *
2524: * Returns 1 if streamable, 0 if not and -1 in case of error.
2525: */
2526: int
2527: xmlPatternStreamable(xmlPatternPtr comp) {
2528: if (comp == NULL)
2529: return(-1);
2530: while (comp != NULL) {
2531: if (comp->stream == NULL)
2532: return(0);
2533: comp = comp->next;
2534: }
2535: return(1);
2536: }
2537:
2538: /**
2539: * xmlPatternMaxDepth:
2540: * @comp: the precompiled pattern
2541: *
2542: * Check the maximum depth reachable by a pattern
2543: *
2544: * Returns -2 if no limit (using //), otherwise the depth,
2545: * and -1 in case of error
2546: */
2547: int
2548: xmlPatternMaxDepth(xmlPatternPtr comp) {
2549: int ret = 0, i;
2550: if (comp == NULL)
2551: return(-1);
2552: while (comp != NULL) {
2553: if (comp->stream == NULL)
2554: return(-1);
2555: for (i = 0;i < comp->stream->nbStep;i++)
2556: if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2557: return(-2);
2558: if (comp->stream->nbStep > ret)
2559: ret = comp->stream->nbStep;
2560: comp = comp->next;
2561: }
2562: return(ret);
2563: }
2564:
2565: /**
2566: * xmlPatternMinDepth:
2567: * @comp: the precompiled pattern
2568: *
2569: * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2570: * part of the set.
2571: *
2572: * Returns -1 in case of error otherwise the depth,
1.1.1.3 ! misho 2573: *
1.1 misho 2574: */
2575: int
2576: xmlPatternMinDepth(xmlPatternPtr comp) {
2577: int ret = 12345678;
2578: if (comp == NULL)
2579: return(-1);
2580: while (comp != NULL) {
2581: if (comp->stream == NULL)
2582: return(-1);
2583: if (comp->stream->nbStep < ret)
2584: ret = comp->stream->nbStep;
2585: if (ret == 0)
2586: return(0);
2587: comp = comp->next;
2588: }
2589: return(ret);
2590: }
2591:
2592: /**
2593: * xmlPatternFromRoot:
2594: * @comp: the precompiled pattern
2595: *
2596: * Check if the pattern must be looked at from the root.
2597: *
2598: * Returns 1 if true, 0 if false and -1 in case of error
2599: */
2600: int
2601: xmlPatternFromRoot(xmlPatternPtr comp) {
2602: if (comp == NULL)
2603: return(-1);
2604: while (comp != NULL) {
2605: if (comp->stream == NULL)
2606: return(-1);
2607: if (comp->flags & PAT_FROM_ROOT)
2608: return(1);
2609: comp = comp->next;
2610: }
2611: return(0);
2612:
2613: }
2614:
2615: #define bottom_pattern
2616: #include "elfgcchack.h"
2617: #endif /* LIBXML_PATTERN_ENABLED */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>