blob: 27e96946b699a91546d314855c176b57257a3f26 [file] [log] [blame]
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001/*
Haibo Huangcfd91dc2020-07-30 23:01:33 -07002 * pattern.c: Implementation of selectors for nodes
Daniel Veillardb3de70c2003-12-02 22:32:15 +00003 *
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
Haibo Huangcfd91dc2020-07-30 23:01:33 -070058* xmlPatternFlags, so be careful 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) {
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700232 xmlFreePatternList(comp);
233}
234
235static void
236xmlFreePatternInternal(xmlPatternPtr comp) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000237 xmlStepOpPtr op;
238 int i;
239
240 if (comp == NULL)
241 return;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000242 if (comp->stream != NULL)
243 xmlFreeStreamComp(comp->stream);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000244 if (comp->pattern != NULL)
245 xmlFree((xmlChar *)comp->pattern);
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000246 if (comp->steps != NULL) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000247 if (comp->dict == NULL) {
248 for (i = 0;i < comp->nbStep;i++) {
249 op = &comp->steps[i];
250 if (op->value != NULL)
251 xmlFree((xmlChar *) op->value);
252 if (op->value2 != NULL)
253 xmlFree((xmlChar *) op->value2);
254 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000255 }
256 xmlFree(comp->steps);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000257 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000258 if (comp->dict != NULL)
259 xmlDictFree(comp->dict);
260
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000261 memset(comp, -1, sizeof(xmlPattern));
262 xmlFree(comp);
263}
264
265/**
266 * xmlFreePatternList:
267 * @comp: an XSLT comp list
268 *
269 * Free up the memory allocated by all the elements of @comp
270 */
271void
272xmlFreePatternList(xmlPatternPtr comp) {
273 xmlPatternPtr cur;
274
275 while (comp != NULL) {
276 cur = comp;
277 comp = comp->next;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +0000278 cur->next = NULL;
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700279 xmlFreePatternInternal(cur);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000280 }
281}
282
283/**
284 * xmlNewPatParserContext:
285 * @pattern: the pattern context
William M. Brackfbb619f2005-06-06 13:49:18 +0000286 * @dict: the inherited dictionary or NULL
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000287 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
288 * with [NULL, NULL] or NULL if no namespace is used
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000289 *
290 * Create a new XML pattern parser context
291 *
292 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
293 */
294static xmlPatParserContextPtr
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000295xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
296 const xmlChar **namespaces) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000297 xmlPatParserContextPtr cur;
298
299 if (pattern == NULL)
300 return(NULL);
301
302 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
303 if (cur == NULL) {
304 ERROR(NULL, NULL, NULL,
305 "xmlNewPatParserContext : malloc failed\n");
306 return(NULL);
307 }
308 memset(cur, 0, sizeof(xmlPatParserContext));
309 cur->dict = dict;
310 cur->cur = pattern;
311 cur->base = pattern;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000312 if (namespaces != NULL) {
313 int i;
Nico Webercedf84d2012-03-05 16:36:59 +0800314 for (i = 0;namespaces[2 * i] != NULL;i++)
315 ;
Daniel Veillardffa7b7e2003-12-05 16:10:21 +0000316 cur->nb_namespaces = i;
317 } else {
318 cur->nb_namespaces = 0;
319 }
320 cur->namespaces = namespaces;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000321 return(cur);
322}
323
324/**
325 * xmlFreePatParserContext:
326 * @ctxt: an XSLT parser context
327 *
328 * Free up the memory allocated by @ctxt
329 */
330static void
331xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
332 if (ctxt == NULL)
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800333 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000334 memset(ctxt, -1, sizeof(xmlPatParserContext));
335 xmlFree(ctxt);
336}
337
338/**
339 * xmlPatternAdd:
340 * @comp: the compiled match expression
341 * @op: an op
342 * @value: the first value
343 * @value2: the second value
344 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000345 * Add a step to an XSLT Compiled Match
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000346 *
347 * Returns -1 in case of failure, 0 otherwise.
348 */
349static int
350xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
351 xmlPatternPtr comp,
352 xmlPatOp op, xmlChar * value, xmlChar * value2)
353{
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000354 if (comp->nbStep >= comp->maxStep) {
355 xmlStepOpPtr temp;
356 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
357 sizeof(xmlStepOp));
358 if (temp == NULL) {
359 ERROR(ctxt, NULL, NULL,
360 "xmlPatternAdd: realloc failed\n");
361 return (-1);
362 }
363 comp->steps = temp;
364 comp->maxStep *= 2;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000365 }
366 comp->steps[comp->nbStep].op = op;
367 comp->steps[comp->nbStep].value = value;
368 comp->steps[comp->nbStep].value2 = value2;
369 comp->nbStep++;
370 return (0);
371}
372
373#if 0
374/**
375 * xsltSwapTopPattern:
376 * @comp: the compiled match expression
377 *
378 * reverse the two top steps.
379 */
380static void
381xsltSwapTopPattern(xmlPatternPtr comp) {
382 int i;
383 int j = comp->nbStep - 1;
384
385 if (j > 0) {
386 register const xmlChar *tmp;
387 register xmlPatOp op;
388 i = j - 1;
389 tmp = comp->steps[i].value;
390 comp->steps[i].value = comp->steps[j].value;
391 comp->steps[j].value = tmp;
392 tmp = comp->steps[i].value2;
393 comp->steps[i].value2 = comp->steps[j].value2;
394 comp->steps[j].value2 = tmp;
395 op = comp->steps[i].op;
396 comp->steps[i].op = comp->steps[j].op;
397 comp->steps[j].op = op;
398 }
399}
400#endif
401
402/**
403 * xmlReversePattern:
404 * @comp: the compiled match expression
405 *
406 * reverse all the stack of expressions
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000407 *
408 * returns 0 in case of success and -1 in case of error.
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000409 */
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000410static int
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000411xmlReversePattern(xmlPatternPtr comp) {
Daniel Veillard56de87e2005-02-16 00:22:29 +0000412 int i, j;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000413
Daniel Veillard56de87e2005-02-16 00:22:29 +0000414 /*
415 * remove the leading // for //a or .//a
416 */
417 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
418 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
419 comp->steps[i].value = comp->steps[j].value;
420 comp->steps[i].value2 = comp->steps[j].value2;
421 comp->steps[i].op = comp->steps[j].op;
422 }
423 comp->nbStep--;
424 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000425 if (comp->nbStep >= comp->maxStep) {
426 xmlStepOpPtr temp;
427 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
428 sizeof(xmlStepOp));
429 if (temp == NULL) {
430 ERROR(ctxt, NULL, NULL,
431 "xmlReversePattern: realloc failed\n");
432 return (-1);
433 }
434 comp->steps = temp;
435 comp->maxStep *= 2;
436 }
Daniel Veillard56de87e2005-02-16 00:22:29 +0000437 i = 0;
438 j = comp->nbStep - 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000439 while (j > i) {
440 register const xmlChar *tmp;
441 register xmlPatOp op;
442 tmp = comp->steps[i].value;
443 comp->steps[i].value = comp->steps[j].value;
444 comp->steps[j].value = tmp;
445 tmp = comp->steps[i].value2;
446 comp->steps[i].value2 = comp->steps[j].value2;
447 comp->steps[j].value2 = tmp;
448 op = comp->steps[i].op;
449 comp->steps[i].op = comp->steps[j].op;
450 comp->steps[j].op = op;
451 j--;
452 i++;
453 }
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000454 comp->steps[comp->nbStep].value = NULL;
455 comp->steps[comp->nbStep].value2 = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000456 comp->steps[comp->nbStep++].op = XML_OP_END;
Daniel Veillardc7c9fb12005-01-12 21:04:15 +0000457 return(0);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000458}
459
460/************************************************************************
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800461 * *
462 * The interpreter for the precompiled patterns *
463 * *
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000464 ************************************************************************/
465
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000466static int
467xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
468 if ((states->states == NULL) || (states->maxstates <= 0)) {
469 states->maxstates = 4;
470 states->nbstates = 0;
471 states->states = xmlMalloc(4 * sizeof(xmlStepState));
472 }
473 else if (states->maxstates <= states->nbstates) {
474 xmlStepState *tmp;
475
476 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
477 2 * states->maxstates * sizeof(xmlStepState));
478 if (tmp == NULL)
479 return(-1);
480 states->states = tmp;
481 states->maxstates *= 2;
482 }
483 states->states[states->nbstates].step = step;
484 states->states[states->nbstates++].node = node;
485#if 0
486 fprintf(stderr, "Push: %d, %s\n", step, node->name);
487#endif
488 return(0);
489}
490
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000491/**
492 * xmlPatMatch:
493 * @comp: the precompiled pattern
494 * @node: a node
495 *
William M. Brackfbb619f2005-06-06 13:49:18 +0000496 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000497 *
498 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
499 */
500static int
501xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
502 int i;
503 xmlStepOpPtr step;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000504 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000505
506 if ((comp == NULL) || (node == NULL)) return(-1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000507 i = 0;
508restart:
509 for (;i < comp->nbStep;i++) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000510 step = &comp->steps[i];
511 switch (step->op) {
512 case XML_OP_END:
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000513 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000514 case XML_OP_ROOT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000515 if (node->type == XML_NAMESPACE_DECL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000516 goto rollback;
Daniel Veillard2fc6df92005-01-30 18:42:55 +0000517 node = node->parent;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000518 if ((node->type == XML_DOCUMENT_NODE) ||
519#ifdef LIBXML_DOCB_ENABLED
520 (node->type == XML_DOCB_DOCUMENT_NODE) ||
521#endif
522 (node->type == XML_HTML_DOCUMENT_NODE))
523 continue;
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000524 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000525 case XML_OP_ELEM:
526 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000527 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000528 if (step->value == NULL)
529 continue;
530 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000531 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000532 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000533 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000534
535 /* Namespace test */
536 if (node->ns == NULL) {
537 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000538 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000539 } else if (node->ns->href != NULL) {
540 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000541 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000542 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000543 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000544 }
545 continue;
546 case XML_OP_CHILD: {
547 xmlNodePtr lst;
548
549 if ((node->type != XML_ELEMENT_NODE) &&
550 (node->type != XML_DOCUMENT_NODE) &&
551#ifdef LIBXML_DOCB_ENABLED
552 (node->type != XML_DOCB_DOCUMENT_NODE) &&
553#endif
554 (node->type != XML_HTML_DOCUMENT_NODE))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000555 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000556
557 lst = node->children;
558
559 if (step->value != NULL) {
560 while (lst != NULL) {
561 if ((lst->type == XML_ELEMENT_NODE) &&
562 (step->value[0] == lst->name[0]) &&
563 (xmlStrEqual(step->value, lst->name)))
564 break;
565 lst = lst->next;
566 }
567 if (lst != NULL)
568 continue;
569 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000570 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000571 }
572 case XML_OP_ATTR:
573 if (node->type != XML_ATTRIBUTE_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000574 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000575 if (step->value != NULL) {
576 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000577 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000578 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000579 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000580 }
581 /* Namespace test */
582 if (node->ns == NULL) {
583 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000584 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000585 } else if (step->value2 != NULL) {
586 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000587 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000588 }
589 continue;
590 case XML_OP_PARENT:
591 if ((node->type == XML_DOCUMENT_NODE) ||
592 (node->type == XML_HTML_DOCUMENT_NODE) ||
593#ifdef LIBXML_DOCB_ENABLED
594 (node->type == XML_DOCB_DOCUMENT_NODE) ||
595#endif
596 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000597 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000598 node = node->parent;
599 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000600 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000601 if (step->value == NULL)
602 continue;
603 if (step->value[0] != node->name[0])
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000604 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000605 if (!xmlStrEqual(step->value, node->name))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000606 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000607 /* Namespace test */
608 if (node->ns == NULL) {
609 if (step->value2 != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000610 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000611 } else if (node->ns->href != NULL) {
612 if (step->value2 == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000613 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000614 if (!xmlStrEqual(step->value2, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000615 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000616 }
617 continue;
618 case XML_OP_ANCESTOR:
619 /* TODO: implement coalescing of ANCESTOR/NODE ops */
620 if (step->value == NULL) {
621 i++;
622 step = &comp->steps[i];
623 if (step->op == XML_OP_ROOT)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000624 goto found;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000625 if (step->op != XML_OP_ELEM)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000626 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000627 if (step->value == NULL)
628 return(-1);
629 }
630 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000631 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000632 if ((node->type == XML_DOCUMENT_NODE) ||
633 (node->type == XML_HTML_DOCUMENT_NODE) ||
634#ifdef LIBXML_DOCB_ENABLED
635 (node->type == XML_DOCB_DOCUMENT_NODE) ||
636#endif
637 (node->type == XML_NAMESPACE_DECL))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000638 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000639 node = node->parent;
640 while (node != NULL) {
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000641 if ((node->type == XML_ELEMENT_NODE) &&
642 (step->value[0] == node->name[0]) &&
643 (xmlStrEqual(step->value, node->name))) {
644 /* Namespace test */
645 if (node->ns == NULL) {
646 if (step->value2 == NULL)
647 break;
648 } else if (node->ns->href != NULL) {
649 if ((step->value2 != NULL) &&
650 (xmlStrEqual(step->value2, node->ns->href)))
651 break;
652 }
653 }
654 node = node->parent;
655 }
656 if (node == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000657 goto rollback;
658 /*
659 * prepare a potential rollback from here
660 * for ancestors of that node.
661 */
662 if (step->op == XML_OP_ANCESTOR)
663 xmlPatPushState(&states, i, node);
664 else
665 xmlPatPushState(&states, i - 1, node);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000666 continue;
667 case XML_OP_NS:
668 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000669 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000670 if (node->ns == NULL) {
671 if (step->value != NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000672 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000673 } else if (node->ns->href != NULL) {
674 if (step->value == NULL)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000675 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000676 if (!xmlStrEqual(step->value, node->ns->href))
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000677 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000678 }
679 break;
680 case XML_OP_ALL:
681 if (node->type != XML_ELEMENT_NODE)
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000682 goto rollback;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000683 break;
684 }
685 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000686found:
687 if (states.states != NULL) {
688 /* Free the rollback states */
689 xmlFree(states.states);
690 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000691 return(1);
Daniel Veillardd4301ab2005-02-03 22:24:10 +0000692rollback:
693 /* got an error try to rollback */
694 if (states.states == NULL)
695 return(0);
696 if (states.nbstates <= 0) {
697 xmlFree(states.states);
698 return(0);
699 }
700 states.nbstates--;
701 i = states.states[states.nbstates].step;
702 node = states.states[states.nbstates].node;
703#if 0
704 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
705#endif
706 goto restart;
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000707}
708
709/************************************************************************
710 * *
711 * Dedicated parser for templates *
712 * *
713 ************************************************************************/
714
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800715#define TODO \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000716 xmlGenericError(xmlGenericErrorContext, \
717 "Unimplemented block at %s:%d\n", \
718 __FILE__, __LINE__);
719#define CUR (*ctxt->cur)
720#define SKIP(val) ctxt->cur += (val)
721#define NXT(val) ctxt->cur[(val)]
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000722#define PEEKPREV(val) ctxt->cur[-(val)]
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000723#define CUR_PTR ctxt->cur
724
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800725#define SKIP_BLANKS \
Daniel Veillard427174f2003-12-10 10:42:59 +0000726 while (IS_BLANK_CH(CUR)) NEXT
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000727
728#define CURRENT (*ctxt->cur)
729#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
730
731
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800732#define PUSH(op, val, val2) \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000733 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
734
735#define XSLT_ERROR(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000736 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000737 ctxt->error = (X); return; }
738
739#define XSLT_ERROR0(X) \
William M. Brackea152c02005-06-09 18:12:28 +0000740 { xsltError(ctxt, __FILE__, __LINE__, X); \
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000741 ctxt->error = (X); return(0); }
742
743#if 0
744/**
745 * xmlPatScanLiteral:
746 * @ctxt: the XPath Parser context
747 *
Haibo Huangcfd91dc2020-07-30 23:01:33 -0700748 * Parse an XPath Literal:
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000749 *
750 * [29] Literal ::= '"' [^"]* '"'
751 * | "'" [^']* "'"
752 *
753 * Returns the Literal parsed or NULL
754 */
755
756static xmlChar *
757xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
758 const xmlChar *q, *cur;
759 xmlChar *ret = NULL;
760 int val, len;
761
762 SKIP_BLANKS;
763 if (CUR == '"') {
764 NEXT;
765 cur = q = CUR_PTR;
766 val = xmlStringCurrentChar(NULL, cur, &len);
767 while ((IS_CHAR(val)) && (val != '"')) {
768 cur += len;
769 val = xmlStringCurrentChar(NULL, cur, &len);
770 }
771 if (!IS_CHAR(val)) {
772 ctxt->error = 1;
773 return(NULL);
774 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000775 if (ctxt->dict)
776 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
777 else
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800778 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000779 }
780 cur += len;
781 CUR_PTR = cur;
782 } else if (CUR == '\'') {
783 NEXT;
784 cur = q = CUR_PTR;
785 val = xmlStringCurrentChar(NULL, cur, &len);
786 while ((IS_CHAR(val)) && (val != '\'')) {
787 cur += len;
788 val = xmlStringCurrentChar(NULL, cur, &len);
789 }
790 if (!IS_CHAR(val)) {
791 ctxt->error = 1;
792 return(NULL);
793 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000794 if (ctxt->dict)
795 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
796 else
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800797 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000798 }
799 cur += len;
800 CUR_PTR = cur;
801 } else {
802 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
803 ctxt->error = 1;
804 return(NULL);
805 }
806 return(ret);
807}
808#endif
809
810/**
811 * xmlPatScanName:
812 * @ctxt: the XPath Parser context
813 *
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800814 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000815 * CombiningChar | Extender
816 *
817 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
818 *
819 * [6] Names ::= Name (S Name)*
820 *
821 * Returns the Name parsed or NULL
822 */
823
824static xmlChar *
825xmlPatScanName(xmlPatParserContextPtr ctxt) {
826 const xmlChar *q, *cur;
827 xmlChar *ret = NULL;
828 int val, len;
829
830 SKIP_BLANKS;
831
832 cur = q = CUR_PTR;
833 val = xmlStringCurrentChar(NULL, cur, &len);
834 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
835 return(NULL);
836
837 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
838 (val == '.') || (val == '-') ||
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800839 (val == '_') ||
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000840 (IS_COMBINING(val)) ||
841 (IS_EXTENDER(val))) {
842 cur += len;
843 val = xmlStringCurrentChar(NULL, cur, &len);
844 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000845 if (ctxt->dict)
846 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
847 else
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800848 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000849 CUR_PTR = cur;
850 return(ret);
851}
852
853/**
854 * xmlPatScanNCName:
855 * @ctxt: the XPath Parser context
856 *
857 * Parses a non qualified name
858 *
859 * Returns the Name parsed or NULL
860 */
861
862static xmlChar *
863xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
864 const xmlChar *q, *cur;
865 xmlChar *ret = NULL;
866 int val, len;
867
868 SKIP_BLANKS;
869
870 cur = q = CUR_PTR;
871 val = xmlStringCurrentChar(NULL, cur, &len);
872 if (!IS_LETTER(val) && (val != '_'))
873 return(NULL);
874
875 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
876 (val == '.') || (val == '-') ||
877 (val == '_') ||
878 (IS_COMBINING(val)) ||
879 (IS_EXTENDER(val))) {
880 cur += len;
881 val = xmlStringCurrentChar(NULL, cur, &len);
882 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000883 if (ctxt->dict)
884 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
885 else
886 ret = xmlStrndup(q, cur - q);
Daniel Veillardb3de70c2003-12-02 22:32:15 +0000887 CUR_PTR = cur;
888 return(ret);
889}
890
891#if 0
892/**
893 * xmlPatScanQName:
894 * @ctxt: the XPath Parser context
895 * @prefix: the place to store the prefix
896 *
897 * Parse a qualified name
898 *
899 * Returns the Name parsed or NULL
900 */
901
902static xmlChar *
903xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
904 xmlChar *ret = NULL;
905
906 *prefix = NULL;
907 ret = xmlPatScanNCName(ctxt);
908 if (CUR == ':') {
909 *prefix = ret;
910 NEXT;
911 ret = xmlPatScanNCName(ctxt);
912 }
913 return(ret);
914}
915#endif
916
917/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000918 * xmlCompileAttributeTest:
919 * @ctxt: the compilation context
920 *
921 * Compile an attribute test.
922 */
923static void
924xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
925 xmlChar *token = NULL;
926 xmlChar *name = NULL;
927 xmlChar *URL = NULL;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800928
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000929 SKIP_BLANKS;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000930 name = xmlPatScanNCName(ctxt);
931 if (name == NULL) {
932 if (CUR == '*') {
933 PUSH(XML_OP_ATTR, NULL, NULL);
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +0000934 NEXT;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000935 } else {
936 ERROR(NULL, NULL, NULL,
937 "xmlCompileAttributeTest : Name expected\n");
938 ctxt->error = 1;
939 }
940 return;
941 }
942 if (CUR == ':') {
943 int i;
944 xmlChar *prefix = name;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800945
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000946 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000947
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800948 if (IS_BLANK_CH(CUR)) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000949 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000950 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +0000951 ctxt->error = 1;
952 goto error;
953 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000954 /*
955 * This is a namespace match
956 */
957 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000958 if ((prefix[0] == 'x') &&
959 (prefix[1] == 'm') &&
960 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000961 (prefix[3] == 0))
962 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800963 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000964 } else {
965 for (i = 0;i < ctxt->nb_namespaces;i++) {
966 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800967 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000968 break;
969 }
970 }
971 if (i >= ctxt->nb_namespaces) {
972 ERROR5(NULL, NULL, NULL,
973 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
974 prefix);
Nick Wellnhofer1f131f12017-05-24 16:36:33 +0200975 XML_PAT_FREE_STRING(ctxt, prefix);
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800976 ctxt->error = 1;
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +0000977 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000978 }
979 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +0000980 XML_PAT_FREE_STRING(ctxt, prefix);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000981 if (token == NULL) {
982 if (CUR == '*') {
983 NEXT;
984 PUSH(XML_OP_ATTR, NULL, URL);
985 } else {
986 ERROR(NULL, NULL, NULL,
987 "xmlCompileAttributeTest : Name expected\n");
988 ctxt->error = 1;
989 goto error;
Daniel Veillardf8e3db02012-09-11 13:26:36 +0800990 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +0000991 } else {
992 PUSH(XML_OP_ATTR, token, URL);
993 }
994 } else {
995 PUSH(XML_OP_ATTR, name, NULL);
996 }
997 return;
998error:
999 if (URL != NULL)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001000 XML_PAT_FREE_STRING(ctxt, URL)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001001 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001002 XML_PAT_FREE_STRING(ctxt, token);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001003}
1004
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001005/**
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001006 * xmlCompileStepPattern:
1007 * @ctxt: the compilation context
1008 *
1009 * Compile the Step Pattern and generates a precompiled
1010 * form suitable for fast matching.
1011 *
1012 * [3] Step ::= '.' | NameTest
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001013 * [4] NameTest ::= QName | '*' | NCName ':' '*'
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001014 */
1015
1016static void
1017xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1018 xmlChar *token = NULL;
1019 xmlChar *name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001020 xmlChar *URL = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001021 int hasBlanks = 0;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001022
1023 SKIP_BLANKS;
1024 if (CUR == '.') {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001025 /*
1026 * Context node.
1027 */
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001028 NEXT;
1029 PUSH(XML_OP_ELEM, NULL, NULL);
1030 return;
1031 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001032 if (CUR == '@') {
1033 /*
1034 * Attribute test.
1035 */
1036 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1037 ERROR5(NULL, NULL, NULL,
1038 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1039 ctxt->error = 1;
1040 return;
1041 }
1042 NEXT;
1043 xmlCompileAttributeTest(ctxt);
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001044 if (ctxt->error != 0)
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001045 goto error;
1046 return;
1047 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001048 name = xmlPatScanNCName(ctxt);
1049 if (name == NULL) {
1050 if (CUR == '*') {
1051 NEXT;
1052 PUSH(XML_OP_ALL, NULL, NULL);
1053 return;
1054 } else {
1055 ERROR(NULL, NULL, NULL,
1056 "xmlCompileStepPattern : Name expected\n");
1057 ctxt->error = 1;
1058 return;
1059 }
1060 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001061 if (IS_BLANK_CH(CUR)) {
1062 hasBlanks = 1;
1063 SKIP_BLANKS;
1064 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001065 if (CUR == ':') {
1066 NEXT;
1067 if (CUR != ':') {
1068 xmlChar *prefix = name;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001069 int i;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001070
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001071 if (hasBlanks || IS_BLANK_CH(CUR)) {
1072 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1073 ctxt->error = 1;
1074 goto error;
1075 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001076 /*
1077 * This is a namespace match
1078 */
1079 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001080 if ((prefix[0] == 'x') &&
1081 (prefix[1] == 'm') &&
1082 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001083 (prefix[3] == 0))
1084 {
1085 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001086 } else {
1087 for (i = 0;i < ctxt->nb_namespaces;i++) {
1088 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001089 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001090 break;
1091 }
Daniel Veillardd4301ab2005-02-03 22:24:10 +00001092 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001093 if (i >= ctxt->nb_namespaces) {
1094 ERROR5(NULL, NULL, NULL,
1095 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1096 prefix);
1097 ctxt->error = 1;
1098 goto error;
1099 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001100 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001101 XML_PAT_FREE_STRING(ctxt, prefix);
Rob Richards3108ba92007-12-06 10:08:52 +00001102 name = NULL;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001103 if (token == NULL) {
1104 if (CUR == '*') {
1105 NEXT;
1106 PUSH(XML_OP_NS, URL, NULL);
1107 } else {
1108 ERROR(NULL, NULL, NULL,
1109 "xmlCompileStepPattern : Name expected\n");
1110 ctxt->error = 1;
1111 goto error;
1112 }
1113 } else {
1114 PUSH(XML_OP_ELEM, token, URL);
1115 }
1116 } else {
1117 NEXT;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001118 if (xmlStrEqual(name, (const xmlChar *) "child")) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001119 XML_PAT_FREE_STRING(ctxt, name);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001120 name = xmlPatScanName(ctxt);
1121 if (name == NULL) {
1122 if (CUR == '*') {
1123 NEXT;
1124 PUSH(XML_OP_ALL, NULL, NULL);
1125 return;
1126 } else {
1127 ERROR(NULL, NULL, NULL,
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001128 "xmlCompileStepPattern : QName expected\n");
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001129 ctxt->error = 1;
1130 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001131 }
1132 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001133 if (CUR == ':') {
1134 xmlChar *prefix = name;
1135 int i;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001136
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001137 NEXT;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001138 if (IS_BLANK_CH(CUR)) {
1139 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1140 ctxt->error = 1;
1141 goto error;
1142 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001143 /*
1144 * This is a namespace match
1145 */
1146 token = xmlPatScanName(ctxt);
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001147 if ((prefix[0] == 'x') &&
1148 (prefix[1] == 'm') &&
1149 (prefix[2] == 'l') &&
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001150 (prefix[3] == 0))
1151 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001152 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001153 } else {
1154 for (i = 0;i < ctxt->nb_namespaces;i++) {
1155 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001156 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001157 break;
1158 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001159 }
Kasimier T. Buchcik627e9a92005-07-22 22:37:35 +00001160 if (i >= ctxt->nb_namespaces) {
1161 ERROR5(NULL, NULL, NULL,
1162 "xmlCompileStepPattern : no namespace bound "
1163 "to prefix %s\n", prefix);
1164 ctxt->error = 1;
1165 goto error;
1166 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001167 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001168 XML_PAT_FREE_STRING(ctxt, prefix);
Rob Richards3108ba92007-12-06 10:08:52 +00001169 name = NULL;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001170 if (token == NULL) {
1171 if (CUR == '*') {
1172 NEXT;
1173 PUSH(XML_OP_NS, URL, NULL);
1174 } else {
1175 ERROR(NULL, NULL, NULL,
1176 "xmlCompileStepPattern : Name expected\n");
1177 ctxt->error = 1;
1178 goto error;
1179 }
1180 } else {
1181 PUSH(XML_OP_CHILD, token, URL);
1182 }
1183 } else
1184 PUSH(XML_OP_CHILD, name, NULL);
1185 return;
1186 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001187 XML_PAT_FREE_STRING(ctxt, name)
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001188 name = NULL;
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001189 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1190 ERROR5(NULL, NULL, NULL,
1191 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1192 ctxt->error = 1;
1193 goto error;
1194 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001195 xmlCompileAttributeTest(ctxt);
1196 if (ctxt->error != 0)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001197 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001198 return;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001199 } else {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001200 ERROR5(NULL, NULL, NULL,
1201 "The 'element' or 'attribute' axis is expected.\n", NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001202 ctxt->error = 1;
1203 goto error;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001204 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001205 }
1206 } else if (CUR == '*') {
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001207 if (name != NULL) {
1208 ctxt->error = 1;
1209 goto error;
1210 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001211 NEXT;
1212 PUSH(XML_OP_ALL, token, NULL);
1213 } else {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001214 PUSH(XML_OP_ELEM, name, NULL);
1215 }
1216 return;
1217error:
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001218 if (URL != NULL)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001219 XML_PAT_FREE_STRING(ctxt, URL)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001220 if (token != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001221 XML_PAT_FREE_STRING(ctxt, token)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001222 if (name != NULL)
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001223 XML_PAT_FREE_STRING(ctxt, name)
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001224}
1225
1226/**
1227 * xmlCompilePathPattern:
1228 * @ctxt: the compilation context
1229 *
1230 * Compile the Path Pattern and generates a precompiled
1231 * form suitable for fast matching.
1232 *
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001233 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001234 */
1235static void
1236xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1237 SKIP_BLANKS;
William M. Brack537f1172005-06-14 22:02:59 +00001238 if (CUR == '/') {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001239 ctxt->comp->flags |= PAT_FROM_ROOT;
William M. Brackea152c02005-06-09 18:12:28 +00001240 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001241 ctxt->comp->flags |= PAT_FROM_CUR;
1242 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001243
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001244 if ((CUR == '/') && (NXT(1) == '/')) {
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 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
Daniel Veillard56de87e2005-02-16 00:22:29 +00001249 PUSH(XML_OP_ANCESTOR, NULL, NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001250 NEXT;
1251 NEXT;
1252 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001253 /* Check for incompleteness. */
1254 SKIP_BLANKS;
1255 if (CUR == 0) {
1256 ERROR5(NULL, NULL, NULL,
1257 "Incomplete expression '%s'.\n", ctxt->base);
1258 ctxt->error = 1;
1259 goto error;
1260 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001261 }
1262 if (CUR == '@') {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001263 NEXT;
1264 xmlCompileAttributeTest(ctxt);
1265 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001266 /* TODO: check for incompleteness */
William M. Brackfbb619f2005-06-06 13:49:18 +00001267 if (CUR != 0) {
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001268 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001269 if (ctxt->error != 0)
1270 goto error;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001271 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001272 } else {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001273 if (CUR == '/') {
1274 PUSH(XML_OP_ROOT, NULL, NULL);
1275 NEXT;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001276 /* Check for incompleteness. */
1277 SKIP_BLANKS;
1278 if (CUR == 0) {
1279 ERROR5(NULL, NULL, NULL,
1280 "Incomplete expression '%s'.\n", ctxt->base);
1281 ctxt->error = 1;
1282 goto error;
1283 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001284 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001285 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001286 if (ctxt->error != 0)
1287 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001288 SKIP_BLANKS;
1289 while (CUR == '/') {
William M. Brackfbb619f2005-06-06 13:49:18 +00001290 if (NXT(1) == '/') {
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001291 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1292 NEXT;
1293 NEXT;
1294 SKIP_BLANKS;
1295 xmlCompileStepPattern(ctxt);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001296 if (ctxt->error != 0)
1297 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001298 } else {
1299 PUSH(XML_OP_PARENT, NULL, NULL);
1300 NEXT;
1301 SKIP_BLANKS;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001302 if (CUR == 0) {
1303 ERROR5(NULL, NULL, NULL,
1304 "Incomplete expression '%s'.\n", ctxt->base);
1305 ctxt->error = 1;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001306 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001307 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001308 xmlCompileStepPattern(ctxt);
1309 if (ctxt->error != 0)
1310 goto error;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001311 }
1312 }
1313 }
Daniel Veillard56de87e2005-02-16 00:22:29 +00001314 if (CUR != 0) {
1315 ERROR5(NULL, NULL, NULL,
1316 "Failed to compile pattern %s\n", ctxt->base);
1317 ctxt->error = 1;
1318 }
Daniel Veillardb3de70c2003-12-02 22:32:15 +00001319error:
1320 return;
1321}
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001322
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001323/**
1324 * xmlCompileIDCXPathPath:
1325 * @ctxt: the compilation context
1326 *
1327 * Compile the Path Pattern and generates a precompiled
1328 * form suitable for fast matching.
1329 *
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001330 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001331 */
1332static void
1333xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1334 SKIP_BLANKS;
1335 if (CUR == '/') {
1336 ERROR5(NULL, NULL, NULL,
1337 "Unexpected selection of the document root in '%s'.\n",
1338 ctxt->base);
1339 goto error;
1340 }
1341 ctxt->comp->flags |= PAT_FROM_CUR;
1342
1343 if (CUR == '.') {
1344 /* "." - "self::node()" */
1345 NEXT;
1346 SKIP_BLANKS;
1347 if (CUR == 0) {
1348 /*
1349 * Selection of the context node.
1350 */
1351 PUSH(XML_OP_ELEM, NULL, NULL);
1352 return;
1353 }
1354 if (CUR != '/') {
1355 /* TODO: A more meaningful error message. */
1356 ERROR5(NULL, NULL, NULL,
1357 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1358 goto error;
1359 }
1360 /* "./" - "self::node()/" */
1361 NEXT;
1362 SKIP_BLANKS;
1363 if (CUR == '/') {
1364 if (IS_BLANK_CH(PEEKPREV(1))) {
1365 /*
1366 * Disallow "./ /"
1367 */
1368 ERROR5(NULL, NULL, NULL,
1369 "Unexpected '/' token in '%s'.\n", ctxt->base);
1370 goto error;
1371 }
1372 /* ".//" - "self:node()/descendant-or-self::node()/" */
1373 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1374 NEXT;
1375 SKIP_BLANKS;
1376 }
1377 if (CUR == 0)
1378 goto error_unfinished;
1379 }
1380 /*
1381 * Process steps.
1382 */
1383 do {
1384 xmlCompileStepPattern(ctxt);
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001385 if (ctxt->error != 0)
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001386 goto error;
1387 SKIP_BLANKS;
1388 if (CUR != '/')
1389 break;
1390 PUSH(XML_OP_PARENT, NULL, NULL);
1391 NEXT;
1392 SKIP_BLANKS;
1393 if (CUR == '/') {
1394 /*
1395 * Disallow subsequent '//'.
1396 */
1397 ERROR5(NULL, NULL, NULL,
1398 "Unexpected subsequent '//' in '%s'.\n",
1399 ctxt->base);
1400 goto error;
1401 }
1402 if (CUR == 0)
1403 goto error_unfinished;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001404
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001405 } while (CUR != 0);
1406
1407 if (CUR != 0) {
1408 ERROR5(NULL, NULL, NULL,
1409 "Failed to compile expression '%s'.\n", ctxt->base);
1410 ctxt->error = 1;
1411 }
1412 return;
1413error:
1414 ctxt->error = 1;
1415 return;
1416
1417error_unfinished:
1418 ctxt->error = 1;
1419 ERROR5(NULL, NULL, NULL,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001420 "Unfinished expression '%s'.\n", ctxt->base);
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00001421 return;
1422}
1423
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001424/************************************************************************
1425 * *
1426 * The streaming code *
1427 * *
1428 ************************************************************************/
1429
1430#ifdef DEBUG_STREAMING
1431static void
1432xmlDebugStreamComp(xmlStreamCompPtr stream) {
1433 int i;
1434
1435 if (stream == NULL) {
1436 printf("Stream: NULL\n");
1437 return;
1438 }
1439 printf("Stream: %d steps\n", stream->nbStep);
1440 for (i = 0;i < stream->nbStep;i++) {
1441 if (stream->steps[i].ns != NULL) {
1442 printf("{%s}", stream->steps[i].ns);
1443 }
1444 if (stream->steps[i].name == NULL) {
1445 printf("* ");
1446 } else {
1447 printf("%s ", stream->steps[i].name);
1448 }
1449 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1450 printf("root ");
1451 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1452 printf("// ");
1453 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1454 printf("final ");
1455 printf("\n");
1456 }
1457}
1458static void
1459xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1460 int i;
1461
1462 if (ctxt == NULL) {
1463 printf("Stream: NULL\n");
1464 return;
1465 }
1466 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1467 if (match)
1468 printf("matches\n");
1469 else
1470 printf("\n");
1471 for (i = 0;i < ctxt->nbState;i++) {
1472 if (ctxt->states[2 * i] < 0)
1473 printf(" %d: free\n", i);
1474 else {
1475 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1476 ctxt->states[(2 * i) + 1]);
1477 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1478 XML_STREAM_STEP_DESC)
1479 printf(" //\n");
1480 else
1481 printf("\n");
1482 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001483 }
1484}
1485#endif
1486/**
1487 * xmlNewStreamComp:
1488 * @size: the number of expected steps
1489 *
1490 * build a new compiled pattern for streaming
1491 *
1492 * Returns the new structure or NULL in case of error.
1493 */
1494static xmlStreamCompPtr
1495xmlNewStreamComp(int size) {
1496 xmlStreamCompPtr cur;
1497
1498 if (size < 4)
1499 size = 4;
1500
1501 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1502 if (cur == NULL) {
1503 ERROR(NULL, NULL, NULL,
1504 "xmlNewStreamComp: malloc failed\n");
1505 return(NULL);
1506 }
1507 memset(cur, 0, sizeof(xmlStreamComp));
1508 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1509 if (cur->steps == NULL) {
1510 xmlFree(cur);
1511 ERROR(NULL, NULL, NULL,
1512 "xmlNewStreamComp: malloc failed\n");
1513 return(NULL);
1514 }
1515 cur->nbStep = 0;
1516 cur->maxStep = size;
1517 return(cur);
1518}
1519
1520/**
1521 * xmlFreeStreamComp:
1522 * @comp: the compiled pattern for streaming
1523 *
1524 * Free the compiled pattern for streaming
1525 */
1526static void
1527xmlFreeStreamComp(xmlStreamCompPtr comp) {
1528 if (comp != NULL) {
1529 if (comp->steps != NULL)
1530 xmlFree(comp->steps);
1531 if (comp->dict != NULL)
1532 xmlDictFree(comp->dict);
1533 xmlFree(comp);
1534 }
1535}
1536
1537/**
1538 * xmlStreamCompAddStep:
1539 * @comp: the compiled pattern for streaming
1540 * @name: the first string, the name, or NULL for *
1541 * @ns: the second step, the namespace name
1542 * @flags: the flags for that step
1543 *
1544 * Add a new step to the compiled pattern
1545 *
1546 * Returns -1 in case of error or the step index if successful
1547 */
1548static int
1549xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001550 const xmlChar *ns, int nodeType, int flags) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001551 xmlStreamStepPtr cur;
1552
1553 if (comp->nbStep >= comp->maxStep) {
1554 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1555 comp->maxStep * 2 * sizeof(xmlStreamStep));
1556 if (cur == NULL) {
1557 ERROR(NULL, NULL, NULL,
1558 "xmlNewStreamComp: malloc failed\n");
1559 return(-1);
1560 }
1561 comp->steps = cur;
1562 comp->maxStep *= 2;
1563 }
1564 cur = &comp->steps[comp->nbStep++];
1565 cur->flags = flags;
1566 cur->name = name;
1567 cur->ns = ns;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001568 cur->nodeType = nodeType;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001569 return(comp->nbStep - 1);
1570}
1571
1572/**
1573 * xmlStreamCompile:
1574 * @comp: the precompiled pattern
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001575 *
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001576 * Tries to stream compile a pattern
1577 *
1578 * Returns -1 in case of failure and 0 in case of success.
1579 */
1580static int
1581xmlStreamCompile(xmlPatternPtr comp) {
1582 xmlStreamCompPtr stream;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001583 int i, s = 0, root = 0, flags = 0, prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001584 xmlStepOp step;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001585
1586 if ((comp == NULL) || (comp->steps == NULL))
1587 return(-1);
Daniel Veillard56de87e2005-02-16 00:22:29 +00001588 /*
1589 * special case for .
1590 */
1591 if ((comp->nbStep == 1) &&
1592 (comp->steps[0].op == XML_OP_ELEM) &&
1593 (comp->steps[0].value == NULL) &&
1594 (comp->steps[0].value2 == NULL)) {
1595 stream = xmlNewStreamComp(0);
1596 if (stream == NULL)
1597 return(-1);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001598 /* Note that the stream will have no steps in this case. */
1599 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
Daniel Veillard56de87e2005-02-16 00:22:29 +00001600 comp->stream = stream;
1601 return(0);
1602 }
1603
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001604 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1605 if (stream == NULL)
1606 return(-1);
1607 if (comp->dict != NULL) {
1608 stream->dict = comp->dict;
1609 xmlDictReference(stream->dict);
1610 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001611
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001612 i = 0;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001613 if (comp->flags & PAT_FROM_ROOT)
1614 stream->flags |= XML_STREAM_FROM_ROOT;
1615
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00001616 for (;i < comp->nbStep;i++) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001617 step = comp->steps[i];
1618 switch (step.op) {
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001619 case XML_OP_END:
1620 break;
1621 case XML_OP_ROOT:
1622 if (i != 0)
1623 goto error;
1624 root = 1;
1625 break;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001626 case XML_OP_NS:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001627 s = xmlStreamCompAddStep(stream, NULL, step.value,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001628 XML_ELEMENT_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001629 if (s < 0)
1630 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001631 prevs = s;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001632 flags = 0;
1633 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001634 case XML_OP_ATTR:
1635 flags |= XML_STREAM_STEP_ATTR;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001636 prevs = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001637 s = xmlStreamCompAddStep(stream,
1638 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001639 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001640 if (s < 0)
1641 goto error;
1642 break;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001643 case XML_OP_ELEM:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001644 if ((step.value == NULL) && (step.value2 == NULL)) {
1645 /*
1646 * We have a "." or "self::node()" here.
1647 * Eliminate redundant self::node() tests like in "/./."
1648 * or "//./"
1649 * The only case we won't eliminate is "//.", i.e. if
1650 * self::node() is the last node test and we had
1651 * continuation somewhere beforehand.
1652 */
1653 if ((comp->nbStep == i + 1) &&
1654 (flags & XML_STREAM_STEP_DESC)) {
1655 /*
1656 * Mark the special case where the expression resolves
1657 * to any type of node.
1658 */
1659 if (comp->nbStep == i + 1) {
1660 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1661 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001662 flags |= XML_STREAM_STEP_NODE;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001663 s = xmlStreamCompAddStep(stream, NULL, NULL,
1664 XML_STREAM_ANY_NODE, flags);
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001665 if (s < 0)
1666 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001667 flags = 0;
1668 /*
1669 * If there was a previous step, mark it to be added to
1670 * the result node-set; this is needed since only
1671 * the last step will be marked as "final" and only
1672 * "final" nodes are added to the resulting set.
1673 */
1674 if (prevs != -1) {
1675 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1676 prevs = -1;
1677 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001678 break;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001679
1680 } else {
1681 /* Just skip this one. */
1682 continue;
1683 }
1684 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001685 /* An element node. */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001686 s = xmlStreamCompAddStep(stream, step.value, step.value2,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001687 XML_ELEMENT_NODE, flags);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001688 if (s < 0)
1689 goto error;
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00001690 prevs = s;
1691 flags = 0;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001692 break;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001693 case XML_OP_CHILD:
1694 /* An element node child. */
1695 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1696 XML_ELEMENT_NODE, flags);
1697 if (s < 0)
1698 goto error;
1699 prevs = s;
1700 flags = 0;
1701 break;
1702 case XML_OP_ALL:
1703 s = xmlStreamCompAddStep(stream, NULL, NULL,
1704 XML_ELEMENT_NODE, flags);
1705 if (s < 0)
1706 goto error;
1707 prevs = s;
1708 flags = 0;
1709 break;
1710 case XML_OP_PARENT:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001711 break;
1712 case XML_OP_ANCESTOR:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001713 /* Skip redundant continuations. */
1714 if (flags & XML_STREAM_STEP_DESC)
1715 break;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001716 flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001717 /*
1718 * Mark the expression as having "//".
1719 */
1720 if ((stream->flags & XML_STREAM_DESC) == 0)
1721 stream->flags |= XML_STREAM_DESC;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001722 break;
1723 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001724 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001725 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1726 /*
1727 * If this should behave like a real pattern, we will mark
1728 * the first step as having "//", to be reentrant on every
1729 * tree level.
1730 */
1731 if ((stream->flags & XML_STREAM_DESC) == 0)
1732 stream->flags |= XML_STREAM_DESC;
1733
1734 if (stream->nbStep > 0) {
1735 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001736 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001737 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001738 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001739 if (stream->nbStep <= s)
1740 goto error;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001741 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1742 if (root)
1743 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1744#ifdef DEBUG_STREAMING
1745 xmlDebugStreamComp(stream);
1746#endif
1747 comp->stream = stream;
1748 return(0);
1749error:
1750 xmlFreeStreamComp(stream);
1751 return(0);
1752}
1753
1754/**
1755 * xmlNewStreamCtxt:
1756 * @size: the number of expected states
1757 *
1758 * build a new stream context
1759 *
1760 * Returns the new structure or NULL in case of error.
1761 */
1762static xmlStreamCtxtPtr
1763xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1764 xmlStreamCtxtPtr cur;
1765
1766 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1767 if (cur == NULL) {
1768 ERROR(NULL, NULL, NULL,
1769 "xmlNewStreamCtxt: malloc failed\n");
1770 return(NULL);
1771 }
1772 memset(cur, 0, sizeof(xmlStreamCtxt));
1773 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1774 if (cur->states == NULL) {
1775 xmlFree(cur);
1776 ERROR(NULL, NULL, NULL,
1777 "xmlNewStreamCtxt: malloc failed\n");
1778 return(NULL);
1779 }
1780 cur->nbState = 0;
1781 cur->maxState = 4;
1782 cur->level = 0;
1783 cur->comp = stream;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001784 cur->blockLevel = -1;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001785 return(cur);
1786}
1787
1788/**
1789 * xmlFreeStreamCtxt:
1790 * @stream: the stream context
1791 *
1792 * Free the stream context
1793 */
1794void
1795xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001796 xmlStreamCtxtPtr next;
1797
1798 while (stream != NULL) {
1799 next = stream->next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001800 if (stream->states != NULL)
1801 xmlFree(stream->states);
1802 xmlFree(stream);
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001803 stream = next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001804 }
1805}
1806
1807/**
1808 * xmlStreamCtxtAddState:
1809 * @comp: the stream context
1810 * @idx: the step index for that streaming state
1811 *
1812 * Add a new state to the stream context
1813 *
1814 * Returns -1 in case of error or the state index if successful
1815 */
1816static int
1817xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1818 int i;
1819 for (i = 0;i < comp->nbState;i++) {
1820 if (comp->states[2 * i] < 0) {
1821 comp->states[2 * i] = idx;
1822 comp->states[2 * i + 1] = level;
1823 return(i);
1824 }
1825 }
1826 if (comp->nbState >= comp->maxState) {
1827 int *cur;
1828
1829 cur = (int *) xmlRealloc(comp->states,
1830 comp->maxState * 4 * sizeof(int));
1831 if (cur == NULL) {
1832 ERROR(NULL, NULL, NULL,
1833 "xmlNewStreamCtxt: malloc failed\n");
1834 return(-1);
1835 }
1836 comp->states = cur;
1837 comp->maxState *= 2;
1838 }
1839 comp->states[2 * comp->nbState] = idx;
1840 comp->states[2 * comp->nbState++ + 1] = level;
1841 return(comp->nbState - 1);
1842}
1843
1844/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001845 * xmlStreamPushInternal:
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001846 * @stream: the stream context
1847 * @name: the current name
1848 * @ns: the namespace name
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001849 * @nodeType: the type of the node
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001850 *
William M. Brackfbb619f2005-06-06 13:49:18 +00001851 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1852 * indicated a dictionary, then strings for name and ns will be expected
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001853 * to come from the dictionary.
1854 * Both @name and @ns being NULL means the / i.e. the root of the document.
1855 * This can also act as a reset.
1856 *
1857 * Returns: -1 in case of error, 1 if the current state in the stream is a
1858 * match and 0 otherwise.
1859 */
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001860static int
1861xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1862 const xmlChar *name, const xmlChar *ns,
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001863 int nodeType) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001864 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001865 xmlStreamCompPtr comp;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001866 xmlStreamStep step;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001867#ifdef DEBUG_STREAMING
1868 xmlStreamCtxtPtr orig = stream;
1869#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001870
1871 if ((stream == NULL) || (stream->nbState < 0))
1872 return(-1);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001873
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001874 while (stream != NULL) {
1875 comp = stream->comp;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001876
1877 if ((nodeType == XML_ELEMENT_NODE) &&
1878 (name == NULL) && (ns == NULL)) {
1879 /* We have a document node here (or a reset). */
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001880 stream->nbState = 0;
1881 stream->level = 0;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001882 stream->blockLevel = -1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001883 if (comp->flags & XML_STREAM_FROM_ROOT) {
1884 if (comp->nbStep == 0) {
1885 /* TODO: We have a "/." here? */
Daniel Veillard2fc6df92005-01-30 18:42:55 +00001886 ret = 1;
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001887 } else {
1888 if ((comp->nbStep == 1) &&
1889 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1890 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1891 {
1892 /*
1893 * In the case of "//." the document node will match
1894 * as well.
1895 */
1896 ret = 1;
1897 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1898 /* TODO: Do we need this ? */
1899 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1900 if (tmp < 0)
1901 err++;
1902 }
1903 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001904 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00001905 stream = stream->next;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001906 continue; /* while */
1907 }
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001908
1909 /*
1910 * Fast check for ".".
1911 */
1912 if (comp->nbStep == 0) {
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001913 /*
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00001914 * / and . are handled at the XPath node set creation
1915 * level by checking min depth
1916 */
1917 if (stream->flags & XML_PATTERN_XPATH) {
1918 stream = stream->next;
1919 continue; /* while */
1920 }
1921 /*
William M. Brackea152c02005-06-09 18:12:28 +00001922 * For non-pattern like evaluation like XML Schema IDCs
1923 * or traditional XPath expressions, this will match if
1924 * we are at the first level only, otherwise on every level.
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001925 */
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001926 if ((nodeType != XML_ATTRIBUTE_NODE) &&
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001927 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1928 (stream->level == 0))) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001929 ret = 1;
Kasimier T. Buchcik22678562005-05-09 16:01:05 +00001930 }
1931 stream->level++;
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00001932 goto stream_next;
1933 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001934 if (stream->blockLevel != -1) {
1935 /*
1936 * Skip blocked expressions.
1937 */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001938 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001939 goto stream_next;
William M. Brackea152c02005-06-09 18:12:28 +00001940 }
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00001941
1942 if ((nodeType != XML_ELEMENT_NODE) &&
1943 (nodeType != XML_ATTRIBUTE_NODE) &&
1944 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1945 /*
1946 * No need to process nodes of other types if we don't
1947 * resolve to those types.
1948 * TODO: Do we need to block the context here?
1949 */
1950 stream->level++;
1951 goto stream_next;
1952 }
1953
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001954 /*
1955 * Check evolution of existing states
1956 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001957 i = 0;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001958 m = stream->nbState;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001959 while (i < m) {
1960 if ((comp->flags & XML_STREAM_DESC) == 0) {
1961 /*
1962 * If there is no "//", then only the last
1963 * added state is of interest.
1964 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001965 stepNr = stream->states[2 * (stream->nbState -1)];
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001966 /*
1967 * TODO: Security check, should not happen, remove it.
1968 */
1969 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1970 stream->level) {
1971 return (-1);
1972 }
1973 desc = 0;
1974 /* loop-stopper */
1975 i = m;
1976 } else {
1977 /*
1978 * If there are "//", then we need to process every "//"
Haibo Huangcfd91dc2020-07-30 23:01:33 -07001979 * occurring in the states, plus any other state for this
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001980 * level.
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001981 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001982 stepNr = stream->states[2 * i];
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001983
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001984 /* TODO: should not happen anymore: dead states */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001985 if (stepNr < 0)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001986 goto next_state;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00001987
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001988 tmp = stream->states[(2 * i) + 1];
1989
1990 /* skip new states just added */
1991 if (tmp > stream->level)
1992 goto next_state;
1993
1994 /* skip states at ancestor levels, except if "//" */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00001995 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00001996 if ((tmp < stream->level) && (!desc))
1997 goto next_state;
1998 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08001999 /*
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002000 * Check for correct node-type.
2001 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002002 step = comp->steps[stepNr];
2003 if (step.nodeType != nodeType) {
2004 if (step.nodeType == XML_ATTRIBUTE_NODE) {
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002005 /*
2006 * Block this expression for deeper evaluation.
2007 */
2008 if ((comp->flags & XML_STREAM_DESC) == 0)
2009 stream->blockLevel = stream->level +1;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002010 goto next_state;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002011 } else if (step.nodeType != XML_STREAM_ANY_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002012 goto next_state;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002013 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002014 /*
2015 * Compare local/namespace-name.
2016 */
2017 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002018 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002019 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002020 } else if (step.name == NULL) {
2021 if (step.ns == NULL) {
2022 /*
2023 * This lets through all elements/attributes.
2024 */
2025 match = 1;
2026 } else if (ns != NULL)
2027 match = xmlStrEqual(step.ns, ns);
2028 } else if (((step.ns != NULL) == (ns != NULL)) &&
2029 (name != NULL) &&
2030 (step.name[0] == name[0]) &&
2031 xmlStrEqual(step.name, name) &&
2032 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2033 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002034 match = 1;
2035 }
2036#if 0
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002037/*
2038* TODO: Pointer comparison won't work, since not guaranteed that the given
2039* values are in the same dict; especially if it's the namespace name,
2040* normally coming from ns->href. We need a namespace dict mechanism !
2041*/
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002042 } else if (comp->dict) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002043 if (step.name == NULL) {
2044 if (step.ns == NULL)
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002045 match = 1;
2046 else
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002047 match = (step.ns == ns);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002048 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002049 match = ((step.name == name) && (step.ns == ns));
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002050 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002051#endif /* if 0 ------------------------------------------------------- */
2052 if (match) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002053 final = step.flags & XML_STREAM_STEP_FINAL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002054 if (desc) {
2055 if (final) {
2056 ret = 1;
2057 } else {
2058 /* descending match create a new state */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002059 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002060 stream->level + 1);
2061 }
2062 } else {
2063 if (final) {
2064 ret = 1;
2065 } else {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002066 xmlStreamCtxtAddState(stream, stepNr + 1,
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002067 stream->level + 1);
2068 }
2069 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002070 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002071 /*
2072 * Check if we have a special case like "foo/bar//.", where
2073 * "foo" is selected as well.
2074 */
2075 ret = 1;
2076 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002077 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002078 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2079 ((! match) || final)) {
2080 /*
2081 * Mark this expression as blocked for any evaluation at
2082 * deeper levels. Note that this includes "/foo"
2083 * expressions if the *pattern* behaviour is used.
2084 */
2085 stream->blockLevel = stream->level +1;
2086 }
2087next_state:
2088 i++;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002089 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002090
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002091 stream->level++;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002092
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002093 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002094 * Re/enter the expression.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002095 * Don't reenter if it's an absolute expression like "/foo",
2096 * except "//foo".
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002097 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002098 step = comp->steps[0];
2099 if (step.flags & XML_STREAM_STEP_ROOT)
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002100 goto stream_next;
2101
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002102 desc = step.flags & XML_STREAM_STEP_DESC;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002103 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2104 /*
2105 * Re/enter the expression if it is a "descendant" one,
2106 * or if we are at the 1st level of evaluation.
2107 */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002108
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002109 if (stream->level == 1) {
2110 if (XML_STREAM_XS_IDC(stream)) {
William M. Brackea152c02005-06-09 18:12:28 +00002111 /*
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002112 * XS-IDC: The missing "self::node()" will always
2113 * match the first given node.
2114 */
William M. Brackea152c02005-06-09 18:12:28 +00002115 goto stream_next;
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002116 } else
2117 goto compare;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002118 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002119 /*
2120 * A "//" is always reentrant.
2121 */
2122 if (desc)
2123 goto compare;
2124
2125 /*
2126 * XS-IDC: Process the 2nd level, since the missing
2127 * "self::node()" is responsible for the 2nd level being
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002128 * the real start level.
2129 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002130 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2131 goto compare;
2132
2133 goto stream_next;
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002134 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002135
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002136compare:
2137 /*
2138 * Check expected node-type.
2139 */
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002140 if (step.nodeType != nodeType) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002141 if (nodeType == XML_ATTRIBUTE_NODE)
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002142 goto stream_next;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002143 else if (step.nodeType != XML_STREAM_ANY_NODE)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002144 goto stream_next;
Kasimier T. Buchcik27820272005-10-14 14:33:48 +00002145 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002146 /*
2147 * Compare local/namespace-name.
2148 */
2149 match = 0;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002150 if (step.nodeType == XML_STREAM_ANY_NODE) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002151 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002152 } else if (step.name == NULL) {
2153 if (step.ns == NULL) {
2154 /*
2155 * This lets through all elements/attributes.
2156 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002157 match = 1;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002158 } else if (ns != NULL)
2159 match = xmlStrEqual(step.ns, ns);
2160 } else if (((step.ns != NULL) == (ns != NULL)) &&
2161 (name != NULL) &&
2162 (step.name[0] == name[0]) &&
2163 xmlStrEqual(step.name, name) &&
2164 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2165 {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002166 match = 1;
2167 }
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002168 final = step.flags & XML_STREAM_STEP_FINAL;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002169 if (match) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002170 if (final)
2171 ret = 1;
2172 else
2173 xmlStreamCtxtAddState(stream, 1, stream->level);
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002174 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
Kasimier T. Buchcikbb80f542006-01-05 14:44:45 +00002175 /*
2176 * Check if we have a special case like "foo//.", where
2177 * "foo" is selected as well.
2178 */
2179 ret = 1;
2180 }
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002181 }
2182 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2183 ((! match) || final)) {
2184 /*
2185 * Mark this expression as blocked for any evaluation at
2186 * deeper levels.
2187 */
2188 stream->blockLevel = stream->level;
2189 }
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002190
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002191stream_next:
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002192 stream = stream->next;
2193 } /* while stream != NULL */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002194
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002195 if (err > 0)
2196 ret = -1;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002197#ifdef DEBUG_STREAMING
2198 xmlDebugStreamCtxt(orig, ret);
2199#endif
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002200 return(ret);
2201}
2202
2203/**
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002204 * xmlStreamPush:
2205 * @stream: the stream context
2206 * @name: the current name
2207 * @ns: the namespace name
2208 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002209 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2210 * indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002211 * to come from the dictionary.
2212 * Both @name and @ns being NULL means the / i.e. the root of the document.
2213 * This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002214 * Otherwise the function will act as if it has been given an element-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002215 *
2216 * Returns: -1 in case of error, 1 if the current state in the stream is a
2217 * match and 0 otherwise.
2218 */
2219int
2220xmlStreamPush(xmlStreamCtxtPtr stream,
2221 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002222 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2223}
2224
2225/**
Daniel Veillard67952602006-01-05 15:29:44 +00002226 * xmlStreamPushNode:
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002227 * @stream: the stream context
2228 * @name: the current name
2229 * @ns: the namespace name
2230 * @nodeType: the type of the node being pushed
2231 *
2232 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2233 * indicated a dictionary, then strings for name and ns will be expected
2234 * to come from the dictionary.
2235 * Both @name and @ns being NULL means the / i.e. the root of the document.
2236 * This can also act as a reset.
2237 * Different from xmlStreamPush() this function can be fed with nodes of type:
2238 * element-, attribute-, text-, cdata-section-, comment- and
2239 * processing-instruction-node.
2240 *
2241 * Returns: -1 in case of error, 1 if the current state in the stream is a
2242 * match and 0 otherwise.
2243 */
2244int
2245xmlStreamPushNode(xmlStreamCtxtPtr stream,
2246 const xmlChar *name, const xmlChar *ns,
2247 int nodeType)
2248{
2249 return (xmlStreamPushInternal(stream, name, ns,
2250 nodeType));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002251}
2252
2253/**
2254* xmlStreamPushAttr:
2255* @stream: the stream context
2256* @name: the current name
2257* @ns: the namespace name
2258*
William M. Brackfbb619f2005-06-06 13:49:18 +00002259* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2260* indicated a dictionary, then strings for name and ns will be expected
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002261* to come from the dictionary.
2262* Both @name and @ns being NULL means the / i.e. the root of the document.
2263* This can also act as a reset.
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002264* Otherwise the function will act as if it has been given an attribute-node.
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002265*
2266* Returns: -1 in case of error, 1 if the current state in the stream is a
2267* match and 0 otherwise.
2268*/
2269int
2270xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2271 const xmlChar *name, const xmlChar *ns) {
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002272 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002273}
2274
2275/**
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002276 * xmlStreamPop:
2277 * @stream: the stream context
2278 *
2279 * push one level from the stream.
2280 *
2281 * Returns: -1 in case of error, 0 otherwise.
2282 */
2283int
2284xmlStreamPop(xmlStreamCtxtPtr stream) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002285 int i, lev;
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002286
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002287 if (stream == NULL)
2288 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002289 while (stream != NULL) {
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002290 /*
2291 * Reset block-level.
2292 */
2293 if (stream->blockLevel == stream->level)
2294 stream->blockLevel = -1;
2295
William M. Brackf8477002008-07-17 05:29:16 +00002296 /*
2297 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2298 * (see the thread at
2299 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2300 */
2301 if (stream->level)
2302 stream->level--;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002303 /*
2304 * Check evolution of existing states
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002305 */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002306 for (i = stream->nbState -1; i >= 0; i--) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002307 /* discard obsoleted states */
Kasimier T. Buchcik9ca11bf2005-06-14 19:24:47 +00002308 lev = stream->states[(2 * i) + 1];
2309 if (lev > stream->level)
2310 stream->nbState--;
2311 if (lev <= stream->level)
2312 break;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002313 }
2314 stream = stream->next;
Daniel Veillard9740d1d2005-02-01 16:21:43 +00002315 }
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002316 return(0);
2317}
2318
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002319/**
2320 * xmlStreamWantsAnyNode:
Daniel Veillard67952602006-01-05 15:29:44 +00002321 * @streamCtxt: the stream context
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002322 *
2323 * Query if the streaming pattern additionally needs to be fed with
2324 * text-, cdata-section-, comment- and processing-instruction-nodes.
2325 * If the result is 0 then only element-nodes and attribute-nodes
2326 * need to be pushed.
2327 *
2328 * Returns: 1 in case of need of nodes of the above described types,
2329 * 0 otherwise. -1 on API errors.
2330 */
2331int
2332xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002333{
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002334 if (streamCtxt == NULL)
2335 return(-1);
2336 while (streamCtxt != NULL) {
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002337 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
Kasimier T. Buchcik97258712006-01-05 12:30:43 +00002338 return(1);
2339 streamCtxt = streamCtxt->next;
2340 }
2341 return(0);
2342}
2343
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002344/************************************************************************
2345 * *
2346 * The public interfaces *
2347 * *
2348 ************************************************************************/
2349
2350/**
2351 * xmlPatterncompile:
2352 * @pattern: the pattern to compile
William M. Brackfbb619f2005-06-06 13:49:18 +00002353 * @dict: an optional dictionary for interned strings
Daniel Veillarded6c5492005-07-23 15:00:22 +00002354 * @flags: compilation flags, see xmlPatternFlags
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002355 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002356 *
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002357 * Compile a pattern.
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002358 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002359 * Returns the compiled form of the pattern or NULL in case of error
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002360 */
2361xmlPatternPtr
Daniel Veillarded6c5492005-07-23 15:00:22 +00002362xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
Daniel Veillardffa7b7e2003-12-05 16:10:21 +00002363 const xmlChar **namespaces) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002364 xmlPatternPtr ret = NULL, cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002365 xmlPatParserContextPtr ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002366 const xmlChar *or, *start;
2367 xmlChar *tmp = NULL;
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002368 int type = 0;
2369 int streamable = 1;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002370
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002371 if (pattern == NULL)
2372 return(NULL);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002373
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002374 start = pattern;
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002375 or = start;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002376 while (*or != 0) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002377 tmp = NULL;
2378 while ((*or != 0) && (*or != '|')) or++;
2379 if (*or == 0)
2380 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2381 else {
2382 tmp = xmlStrndup(start, or - start);
2383 if (tmp != NULL) {
2384 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2385 }
2386 or++;
2387 }
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002388 if (ctxt == NULL) goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002389 cur = xmlNewPattern();
2390 if (cur == NULL) goto error;
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002391 /*
2392 * Assign string dict.
2393 */
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002394 if (dict) {
Kasimier T. Buchcik6ed2eb42006-05-16 15:13:37 +00002395 cur->dict = dict;
2396 xmlDictReference(dict);
2397 }
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002398 if (ret == NULL)
2399 ret = cur;
2400 else {
2401 cur->next = ret->next;
2402 ret->next = cur;
2403 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002404 cur->flags = flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002405 ctxt->comp = cur;
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002406
Kasimier T. Buchcik940ab0c2005-10-19 17:00:53 +00002407 if (XML_STREAM_XS_IDC(cur))
2408 xmlCompileIDCXPathPath(ctxt);
2409 else
2410 xmlCompilePathPattern(ctxt);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002411 if (ctxt->error != 0)
2412 goto error;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002413 xmlFreePatParserContext(ctxt);
William M. Brackfbb619f2005-06-06 13:49:18 +00002414 ctxt = NULL;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002415
2416
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002417 if (streamable) {
2418 if (type == 0) {
2419 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2420 } else if (type == PAT_FROM_ROOT) {
2421 if (cur->flags & PAT_FROM_CUR)
2422 streamable = 0;
2423 } else if (type == PAT_FROM_CUR) {
2424 if (cur->flags & PAT_FROM_ROOT)
2425 streamable = 0;
2426 }
2427 }
2428 if (streamable)
2429 xmlStreamCompile(cur);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002430 if (xmlReversePattern(cur) < 0)
2431 goto error;
William M. Brackea152c02005-06-09 18:12:28 +00002432 if (tmp != NULL) {
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002433 xmlFree(tmp);
William M. Brackea152c02005-06-09 18:12:28 +00002434 tmp = NULL;
2435 }
Daniel Veillard2b2e02d2005-02-05 23:20:22 +00002436 start = or;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002437 }
Daniel Veillardfa1f77f2005-02-21 10:44:36 +00002438 if (streamable == 0) {
2439 cur = ret;
2440 while (cur != NULL) {
2441 if (cur->stream != NULL) {
2442 xmlFreeStreamComp(cur->stream);
2443 cur->stream = NULL;
2444 }
2445 cur = cur->next;
2446 }
2447 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002448
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002449 return(ret);
2450error:
2451 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2452 if (ret != NULL) xmlFreePattern(ret);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002453 if (tmp != NULL) xmlFree(tmp);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002454 return(NULL);
2455}
2456
2457/**
2458 * xmlPatternMatch:
2459 * @comp: the precompiled pattern
2460 * @node: a node
2461 *
William M. Brackfbb619f2005-06-06 13:49:18 +00002462 * Test whether the node matches the pattern
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002463 *
2464 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2465 */
2466int
2467xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2468{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002469 int ret = 0;
2470
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002471 if ((comp == NULL) || (node == NULL))
2472 return(-1);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002473
2474 while (comp != NULL) {
2475 ret = xmlPatMatch(comp, node);
2476 if (ret != 0)
2477 return(ret);
2478 comp = comp->next;
2479 }
2480 return(ret);
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002481}
2482
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002483/**
2484 * xmlPatternGetStreamCtxt:
2485 * @comp: the precompiled pattern
2486 *
2487 * Get a streaming context for that pattern
2488 * Use xmlFreeStreamCtxt to free the context.
2489 *
2490 * Returns a pointer to the context or NULL in case of failure
2491 */
2492xmlStreamCtxtPtr
2493xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2494{
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002495 xmlStreamCtxtPtr ret = NULL, cur;
2496
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002497 if ((comp == NULL) || (comp->stream == NULL))
2498 return(NULL);
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002499
2500 while (comp != NULL) {
2501 if (comp->stream == NULL)
2502 goto failed;
2503 cur = xmlNewStreamCtxt(comp->stream);
2504 if (cur == NULL)
2505 goto failed;
2506 if (ret == NULL)
2507 ret = cur;
2508 else {
2509 cur->next = ret->next;
2510 ret->next = cur;
2511 }
Kasimier T. Buchcik285ebab2005-03-04 18:04:59 +00002512 cur->flags = comp->flags;
Daniel Veillardf1f08cf2005-02-05 16:35:04 +00002513 comp = comp->next;
2514 }
2515 return(ret);
2516failed:
2517 xmlFreeStreamCtxt(ret);
2518 return(NULL);
Daniel Veillard2fc6df92005-01-30 18:42:55 +00002519}
2520
Daniel Veillard56de87e2005-02-16 00:22:29 +00002521/**
2522 * xmlPatternStreamable:
2523 * @comp: the precompiled pattern
2524 *
2525 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2526 * should work.
2527 *
2528 * Returns 1 if streamable, 0 if not and -1 in case of error.
2529 */
2530int
2531xmlPatternStreamable(xmlPatternPtr comp) {
2532 if (comp == NULL)
2533 return(-1);
2534 while (comp != NULL) {
2535 if (comp->stream == NULL)
2536 return(0);
2537 comp = comp->next;
2538 }
2539 return(1);
2540}
2541
2542/**
2543 * xmlPatternMaxDepth:
2544 * @comp: the precompiled pattern
2545 *
2546 * Check the maximum depth reachable by a pattern
2547 *
2548 * Returns -2 if no limit (using //), otherwise the depth,
2549 * and -1 in case of error
2550 */
2551int
2552xmlPatternMaxDepth(xmlPatternPtr comp) {
2553 int ret = 0, i;
2554 if (comp == NULL)
2555 return(-1);
2556 while (comp != NULL) {
2557 if (comp->stream == NULL)
2558 return(-1);
2559 for (i = 0;i < comp->stream->nbStep;i++)
2560 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2561 return(-2);
2562 if (comp->stream->nbStep > ret)
2563 ret = comp->stream->nbStep;
2564 comp = comp->next;
2565 }
2566 return(ret);
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002567}
Daniel Veillard56de87e2005-02-16 00:22:29 +00002568
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002569/**
2570 * xmlPatternMinDepth:
2571 * @comp: the precompiled pattern
2572 *
2573 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2574 * part of the set.
2575 *
2576 * Returns -1 in case of error otherwise the depth,
Daniel Veillardf8e3db02012-09-11 13:26:36 +08002577 *
Daniel Veillardf03a8cd2005-09-04 12:01:57 +00002578 */
2579int
2580xmlPatternMinDepth(xmlPatternPtr comp) {
2581 int ret = 12345678;
2582 if (comp == NULL)
2583 return(-1);
2584 while (comp != NULL) {
2585 if (comp->stream == NULL)
2586 return(-1);
2587 if (comp->stream->nbStep < ret)
2588 ret = comp->stream->nbStep;
2589 if (ret == 0)
2590 return(0);
2591 comp = comp->next;
2592 }
2593 return(ret);
Daniel Veillard56de87e2005-02-16 00:22:29 +00002594}
2595
2596/**
2597 * xmlPatternFromRoot:
2598 * @comp: the precompiled pattern
2599 *
2600 * Check if the pattern must be looked at from the root.
2601 *
2602 * Returns 1 if true, 0 if false and -1 in case of error
2603 */
2604int
2605xmlPatternFromRoot(xmlPatternPtr comp) {
2606 if (comp == NULL)
2607 return(-1);
2608 while (comp != NULL) {
2609 if (comp->stream == NULL)
2610 return(-1);
2611 if (comp->flags & PAT_FROM_ROOT)
2612 return(1);
2613 comp = comp->next;
2614 }
2615 return(0);
2616
2617}
Kasimier T. Buchcik2a0fdd92005-02-17 21:34:45 +00002618
Daniel Veillard5d4644e2005-04-01 13:11:58 +00002619#define bottom_pattern
2620#include "elfgcchack.h"
Daniel Veillardb3de70c2003-12-02 22:32:15 +00002621#endif /* LIBXML_PATTERN_ENABLED */