Coverage Report

Created: 2022-11-15 06:34

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