blob: 33dee3aacef157a40faf6ca242e9dc6edb069beb [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
2 * pattern.c: Implemetation of selectors for nodes
3 *
4 * Reference:
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
Daniel Veillardf8e3db02012-09-11 13:26:36 +08006 * to some extent
Daniel Veillardb3de70c2003-12-02 22:32:15 +00007 * 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
Daniel Veillardf9d16912005-01-30 22:36:30 +000014/*
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 .
Daniel Veillardd4301ab2005-02-03 22:24:10 +000022 * - get rid of the "compile" starting with lowercase
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +000023 * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary
Daniel Veillardf9d16912005-01-30 22:36:30 +000024 */
25
Daniel Veillardb3de70c2003-12-02 22:32:15 +000026#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
Daniel Veillardd4301ab2005-02-03 22:24:10 +000038#ifdef LIBXML_PATTERN_ENABLED
Daniel Veillardb3de70c2003-12-02 22:32:15 +000039
Daniel Veillardd4301ab2005-02-03 22:24:10 +000040/* #define DEBUG_STREAMING */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000041
Patrick R. Gansterer204f1f12012-05-10 20:24:00 +080042#ifdef ERROR
43#undef ERROR
44#endif
Daniel Veillardb3de70c2003-12-02 22:32:15 +000045#define ERROR(a, b, c, d)
46#define ERROR5(a, b, c, d, e)
47
Daniel Veillard2fc6df92005-01-30 18:42:55 +000048#define XML_STREAM_STEP_DESC 1
49#define XML_STREAM_STEP_FINAL 2
50#define XML_STREAM_STEP_ROOT 4
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +000051#define XML_STREAM_STEP_ATTR 8
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000052#define XML_STREAM_STEP_NODE 16
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +000053#define XML_STREAM_STEP_IN_SET 32
Daniel Veillard2fc6df92005-01-30 18:42:55 +000054
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000055/*
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000056* 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
Daniel Veillardf8e3db02012-09-11 13:26:36 +080059* reserved values for xmlPatternFlags.
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000060*/
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000061#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62#define XML_STREAM_FROM_ROOT 1<<15
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000063#define XML_STREAM_DESC 1<<16
64
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000065/*
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
William M. Brackea152c02005-06-09 18:12:28 +000071#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
72 XML_PATTERN_XSSEL | \
73 XML_PATTERN_XSFIELD)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000074
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +000075#define XML_STREAM_XS_IDC(c) ((c)->flags & \
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +000076 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +000077
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +000078#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
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +000082#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
Daniel Veillard2fc6df92005-01-30 18:42:55 +000089typedef struct _xmlStreamStep xmlStreamStep;
90typedef xmlStreamStep *xmlStreamStepPtr;
91struct _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 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +000095 int nodeType; /* type of node */
Daniel Veillard2fc6df92005-01-30 18:42:55 +000096};
97
98typedef struct _xmlStreamComp xmlStreamComp;
99typedef xmlStreamComp *xmlStreamCompPtr;
100struct _xmlStreamComp {
William M. Brackfbb619f2005-06-06 13:49:18 +0000101 xmlDict *dict; /* the dictionary if any */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000102 int nbStep; /* number of steps in the automata */
103 int maxStep; /* allocated number of steps */
104 xmlStreamStepPtr steps; /* the array of steps */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000105 int flags;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000106};
107
108struct _xmlStreamCtxt {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000109 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000110 xmlStreamCompPtr comp; /* the compiled stream */
William M. Brackfbb619f2005-06-06 13:49:18 +0000111 int nbState; /* number of states in the automata */
112 int maxState; /* allocated number of states */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000113 int level; /* how deep are we ? */
114 int *states; /* the array of step indexes */
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +0000115 int flags; /* validation options */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000116 int blockLevel;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000117};
118
119static void xmlFreeStreamComp(xmlStreamCompPtr comp);
120
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000121/*
122 * Types are private:
123 */
124
125typedef 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
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000138typedef struct _xmlStepState xmlStepState;
139typedef xmlStepState *xmlStepStatePtr;
140struct _xmlStepState {
141 int step;
142 xmlNodePtr node;
143};
144
145typedef struct _xmlStepStates xmlStepStates;
146typedef xmlStepStates *xmlStepStatesPtr;
147struct _xmlStepStates {
148 int nbstates;
149 int maxstates;
150 xmlStepStatePtr states;
151};
152
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000153typedef struct _xmlStepOp xmlStepOp;
154typedef xmlStepOp *xmlStepOpPtr;
155struct _xmlStepOp {
156 xmlPatOp op;
157 const xmlChar *value;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000158 const xmlChar *value2; /* The namespace name */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000159};
160
William M. Brackea152c02005-06-09 18:12:28 +0000161#define PAT_FROM_ROOT (1<<8)
162#define PAT_FROM_CUR (1<<9)
Daniel Veillard56de87e2005-02-16 00:22:29 +0000163
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000164struct _xmlPattern {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800165 void *data; /* the associated template */
William M. Brackfbb619f2005-06-06 13:49:18 +0000166 xmlDictPtr dict; /* the optional dictionary */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000167 struct _xmlPattern *next; /* next pattern if | is used */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000168 const xmlChar *pattern; /* the pattern */
Daniel Veillardf5812c32005-09-03 13:43:20 +0000169 int flags; /* flags */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000170 int nbStep;
171 int maxStep;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000172 xmlStepOpPtr steps; /* ops for computation */
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000173 xmlStreamCompPtr stream; /* the streaming data if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000174};
175
176typedef struct _xmlPatParserContext xmlPatParserContext;
177typedef xmlPatParserContext *xmlPatParserContextPtr;
178struct _xmlPatParserContext {
179 const xmlChar *cur; /* the current char being parsed */
180 const xmlChar *base; /* the full expression */
181 int error; /* error code */
William M. Brackfbb619f2005-06-06 13:49:18 +0000182 xmlDictPtr dict; /* the dictionary if any */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000183 xmlPatternPtr comp; /* the result */
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800184 xmlNodePtr elem; /* the current node if any */
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000185 const xmlChar **namespaces; /* the namespaces definitions */
186 int nb_namespaces; /* the number of namespaces */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000187};
188
189/************************************************************************
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800190 * *
191 * Type functions *
192 * *
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000193 ************************************************************************/
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 */
202static xmlPatternPtr
203xmlNewPattern(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;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000214 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 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000221 return(cur);
222}
223
224/**
225 * xmlFreePattern:
226 * @comp: an XSLT comp
227 *
228 * Free up the memory allocated by @comp
229 */
230void
231xmlFreePattern(xmlPatternPtr comp) {
232 xmlStepOpPtr op;
233 int i;
234
235 if (comp == NULL)
236 return;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +0000237 if (comp->next != NULL)
238 xmlFreePattern(comp->next);
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000239 if (comp->stream != NULL)
240 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000241 if (comp->pattern != NULL)
242 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000243 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000244 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 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000252 }
253 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000254 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000255 if (comp->dict != NULL)
256 xmlDictFree(comp->dict);
257
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000258 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 */
268void
269xmlFreePatternList(xmlPatternPtr comp) {
270 xmlPatternPtr cur;
271
272 while (comp != NULL) {
273 cur = comp;
274 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000275 cur->next = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000276 xmlFreePattern(cur);
277 }
278}
279
280/**
281 * xmlNewPatParserContext:
282 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000283 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000284 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
285 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000286 *
287 * Create a new XML pattern parser context
288 *
289 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
290 */
291static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000292xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
293 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000294 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;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000309 if (namespaces != NULL) {
310 int i;
Nico Webercedf84d2012-03-05 16:36:59 +0800311 for (i = 0;namespaces[2 * i] != NULL;i++)
312 ;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000313 cur->nb_namespaces = i;
314 } else {
315 cur->nb_namespaces = 0;
316 }
317 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000318 return(cur);
319}
320
321/**
322 * xmlFreePatParserContext:
323 * @ctxt: an XSLT parser context
324 *
325 * Free up the memory allocated by @ctxt
326 */
327static void
328xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
329 if (ctxt == NULL)
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800330 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000331 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 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000342 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000343 *
344 * Returns -1 in case of failure, 0 otherwise.
345 */
346static int
347xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
348 xmlPatternPtr comp,
349 xmlPatOp op, xmlChar * value, xmlChar * value2)
350{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000351 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;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000362 }
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 */
377static void
378xsltSwapTopPattern(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
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000404 *
405 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000406 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000407static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000408xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000409 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000410
Daniel Veillard56de87e2005-02-16 00:22:29 +0000411 /*
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 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000422 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 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000434 i = 0;
435 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000436 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 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000451 comp->steps[comp->nbStep].value = NULL;
452 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000453 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000454 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000455}
456
457/************************************************************************
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800458 * *
459 * The interpreter for the precompiled patterns *
460 * *
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000461 ************************************************************************/
462
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000463static int
464xmlPatPushState(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
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000488/**
489 * xmlPatMatch:
490 * @comp: the precompiled pattern
491 * @node: a node
492 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000493 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000494 *
495 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
496 */
497static int
498xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
499 int i;
500 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000501 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000502
503 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000504 i = 0;
505restart:
506 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000507 step = &comp->steps[i];
508 switch (step->op) {
509 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000510 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000511 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000512 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000513 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000514 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000515 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;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000521 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000522 case XML_OP_ELEM:
523 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525 if (step->value == NULL)
526 continue;
527 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000528 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000529 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000530 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000531
532 /* Namespace test */
533 if (node->ns == NULL) {
534 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000535 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000536 } else if (node->ns->href != NULL) {
537 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000538 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000539 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000540 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000541 }
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))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000552 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000553
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 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000567 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000568 }
569 case XML_OP_ATTR:
570 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000571 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000572 if (step->value != NULL) {
573 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000574 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000575 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000576 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000577 }
578 /* Namespace test */
579 if (node->ns == NULL) {
580 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000581 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000582 } else if (step->value2 != NULL) {
583 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000584 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000585 }
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))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000594 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000595 node = node->parent;
596 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000597 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000598 if (step->value == NULL)
599 continue;
600 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000601 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000602 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000603 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000604 /* Namespace test */
605 if (node->ns == NULL) {
606 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000607 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000608 } else if (node->ns->href != NULL) {
609 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000610 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000611 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000612 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000613 }
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)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000621 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000622 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000623 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000624 if (step->value == NULL)
625 return(-1);
626 }
627 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000628 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000629 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))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000635 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000636 node = node->parent;
637 while (node != NULL) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000638 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)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000654 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);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000663 continue;
664 case XML_OP_NS:
665 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000666 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000667 if (node->ns == NULL) {
668 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000669 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000670 } else if (node->ns->href != NULL) {
671 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000672 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000673 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000674 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000675 }
676 break;
677 case XML_OP_ALL:
678 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000679 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000680 break;
681 }
682 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000683found:
684 if (states.states != NULL) {
685 /* Free the rollback states */
686 xmlFree(states.states);
687 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000688 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000689rollback:
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;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000704}
705
706/************************************************************************
707 * *
708 * Dedicated parser for templates *
709 * *
710 ************************************************************************/
711
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800712#define TODO \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000713 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)]
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000719#define PEEKPREV(val) ctxt->cur[-(val)]
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000720#define CUR_PTR ctxt->cur
721
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800722#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000723 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000724
725#define CURRENT (*ctxt->cur)
726#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
727
728
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800729#define PUSH(op, val, val2) \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000730 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
731
732#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000733 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000734 ctxt->error = (X); return; }
735
736#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000737 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000738 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
753static xmlChar *
754xmlPatScanLiteral(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 {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000772 if (ctxt->dict)
773 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
774 else
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800775 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000776 }
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 {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000791 if (ctxt->dict)
792 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
793 else
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800794 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000795 }
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 *
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800811 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000812 * 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
821static xmlChar *
822xmlPatScanName(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 == '-') ||
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800836 (val == '_') ||
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000837 (IS_COMBINING(val)) ||
838 (IS_EXTENDER(val))) {
839 cur += len;
840 val = xmlStringCurrentChar(NULL, cur, &len);
841 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000842 if (ctxt->dict)
843 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
844 else
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800845 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000846 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
859static xmlChar *
860xmlPatScanNCName(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 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000880 if (ctxt->dict)
881 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
882 else
883 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000884 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
899static xmlChar *
900xmlPatScanQName(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/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000915 * xmlCompileAttributeTest:
916 * @ctxt: the compilation context
917 *
918 * Compile an attribute test.
919 */
920static void
921xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
922 xmlChar *token = NULL;
923 xmlChar *name = NULL;
924 xmlChar *URL = NULL;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800925
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000926 SKIP_BLANKS;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000927 name = xmlPatScanNCName(ctxt);
928 if (name == NULL) {
929 if (CUR == '*') {
930 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000931 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000932 } 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;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800942
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000943 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000944
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800945 if (IS_BLANK_CH(CUR)) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000946 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000947 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000948 ctxt->error = 1;
949 goto error;
950 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000951 /*
952 * This is a namespace match
953 */
954 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000955 if ((prefix[0] == 'x') &&
956 (prefix[1] == 'm') &&
957 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000958 (prefix[3] == 0))
959 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800960 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000961 } else {
962 for (i = 0;i < ctxt->nb_namespaces;i++) {
963 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800964 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000965 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);
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800972 ctxt->error = 1;
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000973 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000974 }
975 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000976 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000977 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;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800986 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000987 } else {
988 PUSH(XML_OP_ATTR, token, URL);
989 }
990 } else {
991 PUSH(XML_OP_ATTR, name, NULL);
992 }
993 return;
994error:
995 if (URL != NULL)
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800996 XML_PAT_FREE_STRING(ctxt, URL)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000997 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000998 XML_PAT_FREE_STRING(ctxt, token);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000999}
1000
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001001/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001002 * 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
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001009 * [4] NameTest ::= QName | '*' | NCName ':' '*'
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001010 */
1011
1012static void
1013xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1014 xmlChar *token = NULL;
1015 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001016 xmlChar *URL = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001017 int hasBlanks = 0;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001018
1019 SKIP_BLANKS;
1020 if (CUR == '.') {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001021 /*
1022 * Context node.
1023 */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001024 NEXT;
1025 PUSH(XML_OP_ELEM, NULL, NULL);
1026 return;
1027 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001028 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);
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001040 if (ctxt->error != 0)
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001041 goto error;
1042 return;
1043 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001044 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 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001057 if (IS_BLANK_CH(CUR)) {
1058 hasBlanks = 1;
1059 SKIP_BLANKS;
1060 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001061 if (CUR == ':') {
1062 NEXT;
1063 if (CUR != ':') {
1064 xmlChar *prefix = name;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001065 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001066
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001067 if (hasBlanks || IS_BLANK_CH(CUR)) {
1068 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1069 ctxt->error = 1;
1070 goto error;
1071 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001072 /*
1073 * This is a namespace match
1074 */
1075 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001076 if ((prefix[0] == 'x') &&
1077 (prefix[1] == 'm') &&
1078 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001079 (prefix[3] == 0))
1080 {
1081 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001082 } else {
1083 for (i = 0;i < ctxt->nb_namespaces;i++) {
1084 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001085 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001086 break;
1087 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001088 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001089 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 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001096 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001097 XML_PAT_FREE_STRING(ctxt, prefix);
Rob Richards3108ba92007-12-06 10:08:52 +00001098 name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001099 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;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001114 if (xmlStrEqual(name, (const xmlChar *) "child")) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001115 XML_PAT_FREE_STRING(ctxt, name);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001116 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,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001124 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001125 ctxt->error = 1;
1126 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001127 }
1128 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001129 if (CUR == ':') {
1130 xmlChar *prefix = name;
1131 int i;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001132
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001133 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001134 if (IS_BLANK_CH(CUR)) {
1135 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1136 ctxt->error = 1;
1137 goto error;
1138 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001139 /*
1140 * This is a namespace match
1141 */
1142 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001143 if ((prefix[0] == 'x') &&
1144 (prefix[1] == 'm') &&
1145 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001146 (prefix[3] == 0))
1147 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001148 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001149 } else {
1150 for (i = 0;i < ctxt->nb_namespaces;i++) {
1151 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001152 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001153 break;
1154 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001155 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001156 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 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001163 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001164 XML_PAT_FREE_STRING(ctxt, prefix);
Rob Richards3108ba92007-12-06 10:08:52 +00001165 name = NULL;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001166 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")) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001183 XML_PAT_FREE_STRING(ctxt, name)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001184 name = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001185 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 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001191 xmlCompileAttributeTest(ctxt);
1192 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001193 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001194 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001195 } else {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001196 ERROR5(NULL, NULL, NULL,
1197 "The 'element' or 'attribute' axis is expected.\n", NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001198 ctxt->error = 1;
1199 goto error;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001200 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001201 }
1202 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001203 if (name != NULL) {
1204 ctxt->error = 1;
1205 goto error;
1206 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001207 NEXT;
1208 PUSH(XML_OP_ALL, token, NULL);
1209 } else {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001210 PUSH(XML_OP_ELEM, name, NULL);
1211 }
1212 return;
1213error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001214 if (URL != NULL)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001215 XML_PAT_FREE_STRING(ctxt, URL)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001216 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001217 XML_PAT_FREE_STRING(ctxt, token)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001218 if (name != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001219 XML_PAT_FREE_STRING(ctxt, name)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001220}
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 *
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001229 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001230 */
1231static void
1232xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1233 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001234 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001235 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001236 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001237 ctxt->comp->flags |= PAT_FROM_CUR;
1238 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001239
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001240 if ((CUR == '/') && (NXT(1) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001241 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001242 NEXT;
1243 NEXT;
1244 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001245 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001246 NEXT;
1247 NEXT;
1248 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001249 /* 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 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001257 }
1258 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001259 NEXT;
1260 xmlCompileAttributeTest(ctxt);
1261 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001262 /* TODO: check for incompleteness */
William M. Brackfbb619f2005-06-06 13:49:18 +00001263 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001264 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001265 if (ctxt->error != 0)
1266 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001267 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001268 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001269 if (CUR == '/') {
1270 PUSH(XML_OP_ROOT, NULL, NULL);
1271 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001272 /* 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 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001280 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001281 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001282 if (ctxt->error != 0)
1283 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001284 SKIP_BLANKS;
1285 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001286 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001287 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1288 NEXT;
1289 NEXT;
1290 SKIP_BLANKS;
1291 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001292 if (ctxt->error != 0)
1293 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001294 } else {
1295 PUSH(XML_OP_PARENT, NULL, NULL);
1296 NEXT;
1297 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001298 if (CUR == 0) {
1299 ERROR5(NULL, NULL, NULL,
1300 "Incomplete expression '%s'.\n", ctxt->base);
1301 ctxt->error = 1;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001302 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001303 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001304 xmlCompileStepPattern(ctxt);
1305 if (ctxt->error != 0)
1306 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001307 }
1308 }
1309 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001310 if (CUR != 0) {
1311 ERROR5(NULL, NULL, NULL,
1312 "Failed to compile pattern %s\n", ctxt->base);
1313 ctxt->error = 1;
1314 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001315error:
1316 return;
1317}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001318
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001319/**
1320 * xmlCompileIDCXPathPath:
1321 * @ctxt: the compilation context
1322 *
1323 * Compile the Path Pattern and generates a precompiled
1324 * form suitable for fast matching.
1325 *
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001326 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001327 */
1328static void
1329xmlCompileIDCXPathPath(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);
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001381 if (ctxt->error != 0)
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001382 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;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001400
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001401 } 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;
1409error:
1410 ctxt->error = 1;
1411 return;
1412
1413error_unfinished:
1414 ctxt->error = 1;
1415 ERROR5(NULL, NULL, NULL,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001416 "Unfinished expression '%s'.\n", ctxt->base);
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001417 return;
1418}
1419
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001420/************************************************************************
1421 * *
1422 * The streaming code *
1423 * *
1424 ************************************************************************/
1425
1426#ifdef DEBUG_STREAMING
1427static void
1428xmlDebugStreamComp(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}
1454static void
1455xmlDebugStreamCtxt(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 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001479 }
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 */
1490static xmlStreamCompPtr
1491xmlNewStreamComp(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 */
1522static void
1523xmlFreeStreamComp(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 */
1544static int
1545xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001546 const xmlChar *ns, int nodeType, int flags) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001547 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;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001564 cur->nodeType = nodeType;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001565 return(comp->nbStep - 1);
1566}
1567
1568/**
1569 * xmlStreamCompile:
1570 * @comp: the precompiled pattern
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001571 *
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001572 * Tries to stream compile a pattern
1573 *
1574 * Returns -1 in case of failure and 0 in case of success.
1575 */
1576static int
1577xmlStreamCompile(xmlPatternPtr comp) {
1578 xmlStreamCompPtr stream;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001579 int i, s = 0, root = 0, flags = 0, prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001580 xmlStepOp step;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001581
1582 if ((comp == NULL) || (comp->steps == NULL))
1583 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001584 /*
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);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001594 /* Note that the stream will have no steps in this case. */
1595 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001596 comp->stream = stream;
1597 return(0);
1598 }
1599
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001600 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 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001607
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001608 i = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001609 if (comp->flags & PAT_FROM_ROOT)
1610 stream->flags |= XML_STREAM_FROM_ROOT;
1611
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001612 for (;i < comp->nbStep;i++) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001613 step = comp->steps[i];
1614 switch (step.op) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001615 case XML_OP_END:
1616 break;
1617 case XML_OP_ROOT:
1618 if (i != 0)
1619 goto error;
1620 root = 1;
1621 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001622 case XML_OP_NS:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001623 s = xmlStreamCompAddStep(stream, NULL, step.value,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001624 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001625 if (s < 0)
1626 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001627 prevs = s;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001628 flags = 0;
1629 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001630 case XML_OP_ATTR:
1631 flags |= XML_STREAM_STEP_ATTR;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001632 prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001633 s = xmlStreamCompAddStep(stream,
1634 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001635 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001636 if (s < 0)
1637 goto error;
1638 break;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001639 case XML_OP_ELEM:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001640 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 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001658 flags |= XML_STREAM_STEP_NODE;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001659 s = xmlStreamCompAddStep(stream, NULL, NULL,
1660 XML_STREAM_ANY_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001661 if (s < 0)
1662 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001663 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 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001674 break;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001675
1676 } else {
1677 /* Just skip this one. */
1678 continue;
1679 }
1680 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001681 /* An element node. */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001682 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001683 XML_ELEMENT_NODE, flags);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001684 if (s < 0)
1685 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001686 prevs = s;
1687 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001688 break;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001689 case XML_OP_CHILD:
1690 /* An element node child. */
1691 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1692 XML_ELEMENT_NODE, flags);
1693 if (s < 0)
1694 goto error;
1695 prevs = s;
1696 flags = 0;
1697 break;
1698 case XML_OP_ALL:
1699 s = xmlStreamCompAddStep(stream, NULL, NULL,
1700 XML_ELEMENT_NODE, flags);
1701 if (s < 0)
1702 goto error;
1703 prevs = s;
1704 flags = 0;
1705 break;
1706 case XML_OP_PARENT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001707 break;
1708 case XML_OP_ANCESTOR:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001709 /* Skip redundant continuations. */
1710 if (flags & XML_STREAM_STEP_DESC)
1711 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001712 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001713 /*
1714 * Mark the expression as having "//".
1715 */
1716 if ((stream->flags & XML_STREAM_DESC) == 0)
1717 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001718 break;
1719 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001720 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001721 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)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001732 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001733 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001734 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001735 if (stream->nbStep <= s)
1736 goto error;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001737 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);
1745error:
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 */
1758static xmlStreamCtxtPtr
1759xmlNewStreamCtxt(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;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001780 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001781 return(cur);
1782}
1783
1784/**
1785 * xmlFreeStreamCtxt:
1786 * @stream: the stream context
1787 *
1788 * Free the stream context
1789 */
1790void
1791xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001792 xmlStreamCtxtPtr next;
1793
1794 while (stream != NULL) {
1795 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001796 if (stream->states != NULL)
1797 xmlFree(stream->states);
1798 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001799 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001800 }
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 */
1812static int
1813xmlStreamCtxtAddState(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/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001841 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001842 * @stream: the stream context
1843 * @name: the current name
1844 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001845 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001846 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001847 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1848 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001849 * 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 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001856static int
1857xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1858 const xmlChar *name, const xmlChar *ns,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001859 int nodeType) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001860 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001861 xmlStreamCompPtr comp;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001862 xmlStreamStep step;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001863#ifdef DEBUG_STREAMING
1864 xmlStreamCtxtPtr orig = stream;
1865#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001866
1867 if ((stream == NULL) || (stream->nbState < 0))
1868 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001869
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001870 while (stream != NULL) {
1871 comp = stream->comp;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001872
1873 if ((nodeType == XML_ELEMENT_NODE) &&
1874 (name == NULL) && (ns == NULL)) {
1875 /* We have a document node here (or a reset). */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001876 stream->nbState = 0;
1877 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001878 stream->blockLevel = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001879 if (comp->flags & XML_STREAM_FROM_ROOT) {
1880 if (comp->nbStep == 0) {
1881 /* TODO: We have a "/." here? */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001882 ret = 1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001883 } 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 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001900 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001901 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001902 continue; /* while */
1903 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001904
1905 /*
1906 * Fast check for ".".
1907 */
1908 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001909 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001910 * / 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 /*
William M. Brackea152c02005-06-09 18:12:28 +00001918 * 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.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001921 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001922 if ((nodeType != XML_ATTRIBUTE_NODE) &&
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001923 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1924 (stream->level == 0))) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001925 ret = 1;
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001926 }
1927 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001928 goto stream_next;
1929 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001930 if (stream->blockLevel != -1) {
1931 /*
1932 * Skip blocked expressions.
1933 */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001934 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001935 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001936 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001937
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
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001950 /*
1951 * Check evolution of existing states
1952 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001953 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001954 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001955 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 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001961 stepNr = stream->states[2 * (stream->nbState -1)];
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001962 /*
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.
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001977 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001978 stepNr = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001979
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001980 /* TODO: should not happen anymore: dead states */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001981 if (stepNr < 0)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001982 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001983
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001984 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 "//" */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001991 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001992 if ((tmp < stream->level) && (!desc))
1993 goto next_state;
1994 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001995 /*
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001996 * Check for correct node-type.
1997 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001998 step = comp->steps[stepNr];
1999 if (step.nodeType != nodeType) {
2000 if (step.nodeType == XML_ATTRIBUTE_NODE) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002001 /*
2002 * Block this expression for deeper evaluation.
2003 */
2004 if ((comp->flags & XML_STREAM_DESC) == 0)
2005 stream->blockLevel = stream->level +1;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002006 goto next_state;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002007 } else if (step.nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002008 goto next_state;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002009 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002010 /*
2011 * Compare local/namespace-name.
2012 */
2013 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002014 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002015 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002016 } 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 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002030 match = 1;
2031 }
2032#if 0
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002033/*
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*/
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002038 } else if (comp->dict) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002039 if (step.name == NULL) {
2040 if (step.ns == NULL)
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002041 match = 1;
2042 else
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002043 match = (step.ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002044 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002045 match = ((step.name == name) && (step.ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002046 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002047#endif /* if 0 ------------------------------------------------------- */
2048 if (match) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002049 final = step.flags & XML_STREAM_STEP_FINAL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002050 if (desc) {
2051 if (final) {
2052 ret = 1;
2053 } else {
2054 /* descending match create a new state */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002055 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002056 stream->level + 1);
2057 }
2058 } else {
2059 if (final) {
2060 ret = 1;
2061 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002062 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002063 stream->level + 1);
2064 }
2065 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002066 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002067 /*
2068 * Check if we have a special case like "foo/bar//.", where
2069 * "foo" is selected as well.
2070 */
2071 ret = 1;
2072 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002073 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002074 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 }
2083next_state:
2084 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002085 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002086
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002087 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002088
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002089 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002090 * Re/enter the expression.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002091 * Don't reenter if it's an absolute expression like "/foo",
2092 * except "//foo".
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002093 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002094 step = comp->steps[0];
2095 if (step.flags & XML_STREAM_STEP_ROOT)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002096 goto stream_next;
2097
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002098 desc = step.flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002099 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 */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002104
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002105 if (stream->level == 1) {
2106 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00002107 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002108 * XS-IDC: The missing "self::node()" will always
2109 * match the first given node.
2110 */
William M. Brackea152c02005-06-09 18:12:28 +00002111 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002112 } else
2113 goto compare;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002114 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002115 /*
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
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002124 * the real start level.
2125 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002126 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2127 goto compare;
2128
2129 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002130 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002131
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002132compare:
2133 /*
2134 * Check expected node-type.
2135 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002136 if (step.nodeType != nodeType) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002137 if (nodeType == XML_ATTRIBUTE_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002138 goto stream_next;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002139 else if (step.nodeType != XML_STREAM_ANY_NODE)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002140 goto stream_next;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002141 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002142 /*
2143 * Compare local/namespace-name.
2144 */
2145 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002146 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002147 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002148 } else if (step.name == NULL) {
2149 if (step.ns == NULL) {
2150 /*
2151 * This lets through all elements/attributes.
2152 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002153 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002154 } 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 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002162 match = 1;
2163 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002164 final = step.flags & XML_STREAM_STEP_FINAL;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002165 if (match) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002166 if (final)
2167 ret = 1;
2168 else
2169 xmlStreamCtxtAddState(stream, 1, stream->level);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002170 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002171 /*
2172 * Check if we have a special case like "foo//.", where
2173 * "foo" is selected as well.
2174 */
2175 ret = 1;
2176 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002177 }
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 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002186
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002187stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002188 stream = stream->next;
2189 } /* while stream != NULL */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002190
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002191 if (err > 0)
2192 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002193#ifdef DEBUG_STREAMING
2194 xmlDebugStreamCtxt(orig, ret);
2195#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002196 return(ret);
2197}
2198
2199/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002200 * xmlStreamPush:
2201 * @stream: the stream context
2202 * @name: the current name
2203 * @ns: the namespace name
2204 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002205 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2206 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002207 * 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.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002210 * Otherwise the function will act as if it has been given an element-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002211 *
2212 * Returns: -1 in case of error, 1 if the current state in the stream is a
2213 * match and 0 otherwise.
2214 */
2215int
2216xmlStreamPush(xmlStreamCtxtPtr stream,
2217 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002218 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2219}
2220
2221/**
Daniel Veillard67952602006-01-05 15:29:44 +00002222 * xmlStreamPushNode:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002223 * @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 */
2240int
2241xmlStreamPushNode(xmlStreamCtxtPtr stream,
2242 const xmlChar *name, const xmlChar *ns,
2243 int nodeType)
2244{
2245 return (xmlStreamPushInternal(stream, name, ns,
2246 nodeType));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002247}
2248
2249/**
2250* xmlStreamPushAttr:
2251* @stream: the stream context
2252* @name: the current name
2253* @ns: the namespace name
2254*
William M. Brackfbb619f2005-06-06 13:49:18 +00002255* 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
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002257* 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.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002260* Otherwise the function will act as if it has been given an attribute-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002261*
2262* Returns: -1 in case of error, 1 if the current state in the stream is a
2263* match and 0 otherwise.
2264*/
2265int
2266xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2267 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002268 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002269}
2270
2271/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002272 * 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 */
2279int
2280xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002281 int i, lev;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002282
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002283 if (stream == NULL)
2284 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002285 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002286 /*
2287 * Reset block-level.
2288 */
2289 if (stream->blockLevel == stream->level)
2290 stream->blockLevel = -1;
2291
William M. Brackf8477002008-07-17 05:29:16 +00002292 /*
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--;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002299 /*
2300 * Check evolution of existing states
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002301 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002302 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002303 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002304 lev = stream->states[(2 * i) + 1];
2305 if (lev > stream->level)
2306 stream->nbState--;
2307 if (lev <= stream->level)
2308 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002309 }
2310 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00002311 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002312 return(0);
2313}
2314
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002315/**
2316 * xmlStreamWantsAnyNode:
Daniel Veillard67952602006-01-05 15:29:44 +00002317 * @streamCtxt: the stream context
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002318 *
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 */
2327int
2328xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002329{
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002330 if (streamCtxt == NULL)
2331 return(-1);
2332 while (streamCtxt != NULL) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002333 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002334 return(1);
2335 streamCtxt = streamCtxt->next;
2336 }
2337 return(0);
2338}
2339
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002340/************************************************************************
2341 * *
2342 * The public interfaces *
2343 * *
2344 ************************************************************************/
2345
2346/**
2347 * xmlPatterncompile:
2348 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00002349 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00002350 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002351 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002352 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002353 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002354 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002355 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002356 */
2357xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00002358xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002359 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002360 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002361 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002362 const xmlChar *or, *start;
2363 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002364 int type = 0;
2365 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002366
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002367 if (pattern == NULL)
2368 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002369
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002370 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002371 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002372 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002373 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 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002384 if (ctxt == NULL) goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002385 cur = xmlNewPattern();
2386 if (cur == NULL) goto error;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002387 /*
2388 * Assign string dict.
2389 */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002390 if (dict) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002391 cur->dict = dict;
2392 xmlDictReference(dict);
2393 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002394 if (ret == NULL)
2395 ret = cur;
2396 else {
2397 cur->next = ret->next;
2398 ret->next = cur;
2399 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002400 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002401 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002402
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002403 if (XML_STREAM_XS_IDC(cur))
2404 xmlCompileIDCXPathPath(ctxt);
2405 else
2406 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002407 if (ctxt->error != 0)
2408 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002409 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002410 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002411
2412
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002413 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);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002426 if (xmlReversePattern(cur) < 0)
2427 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002428 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002429 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002430 tmp = NULL;
2431 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002432 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002433 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002434 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 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002444
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002445 return(ret);
2446error:
2447 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2448 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002449 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002450 return(NULL);
2451}
2452
2453/**
2454 * xmlPatternMatch:
2455 * @comp: the precompiled pattern
2456 * @node: a node
2457 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002458 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002459 *
2460 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2461 */
2462int
2463xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2464{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002465 int ret = 0;
2466
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002467 if ((comp == NULL) || (node == NULL))
2468 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002469
2470 while (comp != NULL) {
2471 ret = xmlPatMatch(comp, node);
2472 if (ret != 0)
2473 return(ret);
2474 comp = comp->next;
2475 }
2476 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002477}
2478
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002479/**
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 */
2488xmlStreamCtxtPtr
2489xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2490{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002491 xmlStreamCtxtPtr ret = NULL, cur;
2492
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002493 if ((comp == NULL) || (comp->stream == NULL))
2494 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002495
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 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002508 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002509 comp = comp->next;
2510 }
2511 return(ret);
2512failed:
2513 xmlFreeStreamCtxt(ret);
2514 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002515}
2516
Daniel Veillard56de87e2005-02-16 00:22:29 +00002517/**
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 */
2526int
2527xmlPatternStreamable(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 */
2547int
2548xmlPatternMaxDepth(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);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002563}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002564
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002565/**
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,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002573 *
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002574 */
2575int
2576xmlPatternMinDepth(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);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002590}
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 */
2600int
2601xmlPatternFromRoot(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}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002614
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002615#define bottom_pattern
2616#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002617#endif /* LIBXML_PATTERN_ENABLED */