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