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