Coverage Report

Created: 2023-06-07 06:14

/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
1.21M
#define XML_STREAM_STEP_DESC  1
49
94.3k
#define XML_STREAM_STEP_FINAL 2
50
94.2k
#define XML_STREAM_STEP_ROOT  4
51
0
#define XML_STREAM_STEP_ATTR  8
52
1
#define XML_STREAM_STEP_NODE  16
53
132
#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
2.82k
#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62
93
#define XML_STREAM_FROM_ROOT 1<<15
63
94.9k
#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
97.3k
#define XML_STREAM_ANY_NODE 100
70
71
96.0k
#define XML_PATTERN_NOTPATTERN  (XML_PATTERN_XPATH | \
72
96.0k
         XML_PATTERN_XSSEL | \
73
96.0k
         XML_PATTERN_XSFIELD)
74
75
91.6k
#define XML_STREAM_XS_IDC(c) ((c)->flags & \
76
91.6k
    (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
77
78
0
#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
0
    if ((c)->comp->dict) \
84
0
  r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
85
0
    else r = xmlStrdup(BAD_CAST nsname);
86
87
0
#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
2.84k
#define PAT_FROM_ROOT (1<<8)
162
1.96k
#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
1.58k
xmlNewPattern(void) {
204
1.58k
    xmlPatternPtr cur;
205
206
1.58k
    cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
207
1.58k
    if (cur == NULL) {
208
0
  ERROR(NULL, NULL, NULL,
209
0
    "xmlNewPattern : malloc failed\n");
210
0
  return(NULL);
211
0
    }
212
1.58k
    memset(cur, 0, sizeof(xmlPattern));
213
1.58k
    cur->maxStep = 10;
214
1.58k
    cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
215
1.58k
    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
1.58k
    return(cur);
222
1.58k
}
223
224
/**
225
 * xmlFreePattern:
226
 * @comp:  an XSLT comp
227
 *
228
 * Free up the memory allocated by @comp
229
 */
230
void
231
2.35k
xmlFreePattern(xmlPatternPtr comp) {
232
2.35k
    xmlFreePatternList(comp);
233
2.35k
}
234
235
static void
236
1.58k
xmlFreePatternInternal(xmlPatternPtr comp) {
237
1.58k
    xmlStepOpPtr op;
238
1.58k
    int i;
239
240
1.58k
    if (comp == NULL)
241
0
  return;
242
1.58k
    if (comp->stream != NULL)
243
431
        xmlFreeStreamComp(comp->stream);
244
1.58k
    if (comp->pattern != NULL)
245
0
  xmlFree((xmlChar *)comp->pattern);
246
1.58k
    if (comp->steps != NULL) {
247
1.58k
        if (comp->dict == NULL) {
248
17.3M
      for (i = 0;i < comp->nbStep;i++) {
249
17.3M
    op = &comp->steps[i];
250
17.3M
    if (op->value != NULL)
251
5.81M
        xmlFree((xmlChar *) op->value);
252
17.3M
    if (op->value2 != NULL)
253
0
        xmlFree((xmlChar *) op->value2);
254
17.3M
      }
255
1.58k
  }
256
1.58k
  xmlFree(comp->steps);
257
1.58k
    }
258
1.58k
    if (comp->dict != NULL)
259
0
        xmlDictFree(comp->dict);
260
261
1.58k
    memset(comp, -1, sizeof(xmlPattern));
262
1.58k
    xmlFree(comp);
263
1.58k
}
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
2.76k
xmlFreePatternList(xmlPatternPtr comp) {
273
2.76k
    xmlPatternPtr cur;
274
275
4.34k
    while (comp != NULL) {
276
1.58k
  cur = comp;
277
1.58k
  comp = comp->next;
278
1.58k
  cur->next = NULL;
279
1.58k
  xmlFreePatternInternal(cur);
280
1.58k
    }
281
2.76k
}
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
1.58k
                       const xmlChar **namespaces) {
297
1.58k
    xmlPatParserContextPtr cur;
298
299
1.58k
    if (pattern == NULL)
300
0
        return(NULL);
301
302
1.58k
    cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
303
1.58k
    if (cur == NULL) {
304
0
  ERROR(NULL, NULL, NULL,
305
0
    "xmlNewPatParserContext : malloc failed\n");
306
0
  return(NULL);
307
0
    }
308
1.58k
    memset(cur, 0, sizeof(xmlPatParserContext));
309
1.58k
    cur->dict = dict;
310
1.58k
    cur->cur = pattern;
311
1.58k
    cur->base = pattern;
312
1.58k
    if (namespaces != NULL) {
313
0
        int i;
314
0
        for (i = 0;namespaces[2 * i] != NULL;i++)
315
0
            ;
316
0
        cur->nb_namespaces = i;
317
1.58k
    } else {
318
1.58k
        cur->nb_namespaces = 0;
319
1.58k
    }
320
1.58k
    cur->namespaces = namespaces;
321
1.58k
    return(cur);
322
1.58k
}
323
324
/**
325
 * xmlFreePatParserContext:
326
 * @ctxt:  an XSLT parser context
327
 *
328
 * Free up the memory allocated by @ctxt
329
 */
330
static void
331
1.58k
xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
332
1.58k
    if (ctxt == NULL)
333
0
  return;
334
1.58k
    memset(ctxt, -1, sizeof(xmlPatParserContext));
335
1.58k
    xmlFree(ctxt);
336
1.58k
}
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
17.3M
{
354
17.3M
    if (comp->nbStep >= comp->maxStep) {
355
1.37k
        xmlStepOpPtr temp;
356
1.37k
  temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
357
1.37k
                                   sizeof(xmlStepOp));
358
1.37k
        if (temp == NULL) {
359
0
      ERROR(ctxt, NULL, NULL,
360
0
           "xmlPatternAdd: realloc failed\n");
361
0
      return (-1);
362
0
  }
363
1.37k
  comp->steps = temp;
364
1.37k
  comp->maxStep *= 2;
365
1.37k
    }
366
17.3M
    comp->steps[comp->nbStep].op = op;
367
17.3M
    comp->steps[comp->nbStep].value = value;
368
17.3M
    comp->steps[comp->nbStep].value2 = value2;
369
17.3M
    comp->nbStep++;
370
17.3M
    return (0);
371
17.3M
}
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
431
xmlReversePattern(xmlPatternPtr comp) {
412
431
    int i, j;
413
414
    /*
415
     * remove the leading // for //a or .//a
416
     */
417
431
    if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
418
10
        for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
419
5
      comp->steps[i].value = comp->steps[j].value;
420
5
      comp->steps[i].value2 = comp->steps[j].value2;
421
5
      comp->steps[i].op = comp->steps[j].op;
422
5
  }
423
5
  comp->nbStep--;
424
5
    }
425
431
    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
431
    i = 0;
438
431
    j = comp->nbStep - 1;
439
8.61M
    while (j > i) {
440
8.61M
  register const xmlChar *tmp;
441
8.61M
  register xmlPatOp op;
442
8.61M
  tmp = comp->steps[i].value;
443
8.61M
  comp->steps[i].value = comp->steps[j].value;
444
8.61M
  comp->steps[j].value = tmp;
445
8.61M
  tmp = comp->steps[i].value2;
446
8.61M
  comp->steps[i].value2 = comp->steps[j].value2;
447
8.61M
  comp->steps[j].value2 = tmp;
448
8.61M
  op = comp->steps[i].op;
449
8.61M
  comp->steps[i].op = comp->steps[j].op;
450
8.61M
  comp->steps[j].op = op;
451
8.61M
  j--;
452
8.61M
  i++;
453
8.61M
    }
454
431
    comp->steps[comp->nbStep].value = NULL;
455
431
    comp->steps[comp->nbStep].value2 = NULL;
456
431
    comp->steps[comp->nbStep++].op = XML_OP_END;
457
431
    return(0);
458
431
}
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
49.3M
#define CUR (*ctxt->cur)
708
#define SKIP(val) ctxt->cur += (val)
709
8.69M
#define NXT(val) ctxt->cur[(val)]
710
#define PEEKPREV(val) ctxt->cur[-(val)]
711
14.5M
#define CUR_PTR ctxt->cur
712
713
#define SKIP_BLANKS             \
714
26.0M
    while (IS_BLANK_CH(CUR)) NEXT
715
716
#define CURRENT (*ctxt->cur)
717
20.2M
#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
718
719
720
#define PUSH(op, val, val2)           \
721
17.3M
    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
0
xmlPatScanName(xmlPatParserContextPtr ctxt) {
814
0
    const xmlChar *q, *cur;
815
0
    xmlChar *ret = NULL;
816
0
    int val, len;
817
818
0
    SKIP_BLANKS;
819
820
0
    cur = q = CUR_PTR;
821
0
    val = xmlStringCurrentChar(NULL, cur, &len);
822
0
    if (!IS_LETTER(val) && (val != '_') && (val != ':'))
823
0
  return(NULL);
824
825
0
    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
826
0
           (val == '.') || (val == '-') ||
827
0
     (val == '_') ||
828
0
     (IS_COMBINING(val)) ||
829
0
     (IS_EXTENDER(val))) {
830
0
  cur += len;
831
0
  val = xmlStringCurrentChar(NULL, cur, &len);
832
0
    }
833
0
    if (ctxt->dict)
834
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
835
0
    else
836
0
  ret = xmlStrndup(q, cur - q);
837
0
    CUR_PTR = cur;
838
0
    return(ret);
839
0
}
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
8.69M
xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
852
8.69M
    const xmlChar *q, *cur;
853
8.69M
    xmlChar *ret = NULL;
854
8.69M
    int val, len;
855
856
8.69M
    SKIP_BLANKS;
857
858
8.69M
    cur = q = CUR_PTR;
859
8.69M
    val = xmlStringCurrentChar(NULL, cur, &len);
860
8.69M
    if (!IS_LETTER(val) && (val != '_'))
861
2.88M
  return(NULL);
862
863
16.5M
    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
864
16.5M
           (val == '.') || (val == '-') ||
865
16.5M
     (val == '_') ||
866
16.5M
     (IS_COMBINING(val)) ||
867
16.5M
     (IS_EXTENDER(val))) {
868
10.7M
  cur += len;
869
10.7M
  val = xmlStringCurrentChar(NULL, cur, &len);
870
10.7M
    }
871
5.81M
    if (ctxt->dict)
872
0
  ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
873
5.81M
    else
874
5.81M
  ret = xmlStrndup(q, cur - q);
875
5.81M
    CUR_PTR = cur;
876
5.81M
    return(ret);
877
8.69M
}
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
0
xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
913
0
    xmlChar *token = NULL;
914
0
    xmlChar *name = NULL;
915
0
    xmlChar *URL = NULL;
916
917
0
    SKIP_BLANKS;
918
0
    name = xmlPatScanNCName(ctxt);
919
0
    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
0
    if (CUR == ':') {
931
0
  int i;
932
0
  xmlChar *prefix = name;
933
934
0
  NEXT;
935
936
0
  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
0
  token = xmlPatScanName(ctxt);
946
0
  if ((prefix[0] == 'x') &&
947
0
      (prefix[1] == 'm') &&
948
0
      (prefix[2] == 'l') &&
949
0
      (prefix[3] == 0))
950
0
  {
951
0
      XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
952
0
  } else {
953
0
      for (i = 0;i < ctxt->nb_namespaces;i++) {
954
0
    if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
955
0
        XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
956
0
        break;
957
0
    }
958
0
      }
959
0
      if (i >= ctxt->nb_namespaces) {
960
0
    ERROR5(NULL, NULL, NULL,
961
0
        "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
962
0
        prefix);
963
0
          XML_PAT_FREE_STRING(ctxt, prefix);
964
0
    ctxt->error = 1;
965
0
    goto error;
966
0
      }
967
0
  }
968
0
  XML_PAT_FREE_STRING(ctxt, prefix);
969
0
  if (token == NULL) {
970
0
      if (CUR == '*') {
971
0
    NEXT;
972
0
    PUSH(XML_OP_ATTR, NULL, URL);
973
0
      } else {
974
0
    ERROR(NULL, NULL, NULL,
975
0
        "xmlCompileAttributeTest : Name expected\n");
976
0
    ctxt->error = 1;
977
0
    goto error;
978
0
      }
979
0
  } else {
980
0
      PUSH(XML_OP_ATTR, token, URL);
981
0
  }
982
0
    } else {
983
0
  PUSH(XML_OP_ATTR, name, NULL);
984
0
    }
985
0
    return;
986
0
error:
987
0
    if (URL != NULL)
988
0
  XML_PAT_FREE_STRING(ctxt, URL)
989
0
    if (token != NULL)
990
0
  XML_PAT_FREE_STRING(ctxt, token);
991
0
}
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
8.69M
xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1006
8.69M
    xmlChar *token = NULL;
1007
8.69M
    xmlChar *name = NULL;
1008
8.69M
    xmlChar *URL = NULL;
1009
8.69M
    int hasBlanks = 0;
1010
1011
8.69M
    SKIP_BLANKS;
1012
8.69M
    if (CUR == '.') {
1013
  /*
1014
  * Context node.
1015
  */
1016
370
  NEXT;
1017
370
  PUSH(XML_OP_ELEM, NULL, NULL);
1018
370
  return;
1019
370
    }
1020
8.69M
    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
8.69M
    name = xmlPatScanNCName(ctxt);
1037
8.69M
    if (name == NULL) {
1038
2.88M
  if (CUR == '*') {
1039
2.88M
      NEXT;
1040
2.88M
      PUSH(XML_OP_ALL, NULL, NULL);
1041
2.88M
      return;
1042
2.88M
  } else {
1043
848
      ERROR(NULL, NULL, NULL,
1044
848
        "xmlCompileStepPattern : Name expected\n");
1045
848
      ctxt->error = 1;
1046
848
      return;
1047
848
  }
1048
2.88M
    }
1049
5.81M
    if (IS_BLANK_CH(CUR)) {
1050
24
  hasBlanks = 1;
1051
24
  SKIP_BLANKS;
1052
24
    }
1053
5.81M
    if (CUR == ':') {
1054
0
  NEXT;
1055
0
  if (CUR != ':') {
1056
0
      xmlChar *prefix = name;
1057
0
      int i;
1058
1059
0
      if (hasBlanks || IS_BLANK_CH(CUR)) {
1060
0
    ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1061
0
    ctxt->error = 1;
1062
0
    goto error;
1063
0
      }
1064
      /*
1065
       * This is a namespace match
1066
       */
1067
0
      token = xmlPatScanName(ctxt);
1068
0
      if ((prefix[0] == 'x') &&
1069
0
    (prefix[1] == 'm') &&
1070
0
    (prefix[2] == 'l') &&
1071
0
    (prefix[3] == 0))
1072
0
      {
1073
0
    XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1074
0
      } else {
1075
0
    for (i = 0;i < ctxt->nb_namespaces;i++) {
1076
0
        if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1077
0
      XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1078
0
      break;
1079
0
        }
1080
0
    }
1081
0
    if (i >= ctxt->nb_namespaces) {
1082
0
        ERROR5(NULL, NULL, NULL,
1083
0
      "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1084
0
      prefix);
1085
0
        ctxt->error = 1;
1086
0
        goto error;
1087
0
    }
1088
0
      }
1089
0
      XML_PAT_FREE_STRING(ctxt, prefix);
1090
0
      name = NULL;
1091
0
      if (token == NULL) {
1092
0
    if (CUR == '*') {
1093
0
        NEXT;
1094
0
        PUSH(XML_OP_NS, URL, NULL);
1095
0
    } else {
1096
0
        ERROR(NULL, NULL, NULL,
1097
0
          "xmlCompileStepPattern : Name expected\n");
1098
0
        ctxt->error = 1;
1099
0
        goto error;
1100
0
    }
1101
0
      } else {
1102
0
    PUSH(XML_OP_ELEM, token, URL);
1103
0
      }
1104
0
  } else {
1105
0
      NEXT;
1106
0
      if (xmlStrEqual(name, (const xmlChar *) "child")) {
1107
0
    XML_PAT_FREE_STRING(ctxt, name);
1108
0
    name = xmlPatScanName(ctxt);
1109
0
    if (name == NULL) {
1110
0
        if (CUR == '*') {
1111
0
      NEXT;
1112
0
      PUSH(XML_OP_ALL, NULL, NULL);
1113
0
      return;
1114
0
        } else {
1115
0
      ERROR(NULL, NULL, NULL,
1116
0
          "xmlCompileStepPattern : QName expected\n");
1117
0
      ctxt->error = 1;
1118
0
      goto error;
1119
0
        }
1120
0
    }
1121
0
    if (CUR == ':') {
1122
0
        xmlChar *prefix = name;
1123
0
        int i;
1124
1125
0
        NEXT;
1126
0
        if (IS_BLANK_CH(CUR)) {
1127
0
      ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1128
0
      ctxt->error = 1;
1129
0
      goto error;
1130
0
        }
1131
        /*
1132
        * This is a namespace match
1133
        */
1134
0
        token = xmlPatScanName(ctxt);
1135
0
        if ((prefix[0] == 'x') &&
1136
0
      (prefix[1] == 'm') &&
1137
0
      (prefix[2] == 'l') &&
1138
0
      (prefix[3] == 0))
1139
0
        {
1140
0
      XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1141
0
        } else {
1142
0
      for (i = 0;i < ctxt->nb_namespaces;i++) {
1143
0
          if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1144
0
        XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1145
0
        break;
1146
0
          }
1147
0
      }
1148
0
      if (i >= ctxt->nb_namespaces) {
1149
0
          ERROR5(NULL, NULL, NULL,
1150
0
        "xmlCompileStepPattern : no namespace bound "
1151
0
        "to prefix %s\n", prefix);
1152
0
          ctxt->error = 1;
1153
0
          goto error;
1154
0
      }
1155
0
        }
1156
0
        XML_PAT_FREE_STRING(ctxt, prefix);
1157
0
        name = NULL;
1158
0
        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
0
        } else {
1169
0
      PUSH(XML_OP_CHILD, token, URL);
1170
0
        }
1171
0
    } else
1172
0
        PUSH(XML_OP_CHILD, name, NULL);
1173
0
    return;
1174
0
      } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1175
0
    XML_PAT_FREE_STRING(ctxt, name)
1176
0
    name = NULL;
1177
0
    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
0
    xmlCompileAttributeTest(ctxt);
1184
0
    if (ctxt->error != 0)
1185
0
        goto error;
1186
0
    return;
1187
0
      } else {
1188
0
    ERROR5(NULL, NULL, NULL,
1189
0
        "The 'element' or 'attribute' axis is expected.\n", NULL);
1190
0
    ctxt->error = 1;
1191
0
    goto error;
1192
0
      }
1193
0
  }
1194
5.81M
    } else if (CUR == '*') {
1195
0
        if (name != NULL) {
1196
0
      ctxt->error = 1;
1197
0
      goto error;
1198
0
  }
1199
0
  NEXT;
1200
0
  PUSH(XML_OP_ALL, token, NULL);
1201
5.81M
    } else {
1202
5.81M
  PUSH(XML_OP_ELEM, name, NULL);
1203
5.81M
    }
1204
5.81M
    return;
1205
5.81M
error:
1206
0
    if (URL != NULL)
1207
0
  XML_PAT_FREE_STRING(ctxt, URL)
1208
0
    if (token != NULL)
1209
0
  XML_PAT_FREE_STRING(ctxt, token)
1210
0
    if (name != NULL)
1211
0
  XML_PAT_FREE_STRING(ctxt, name)
1212
0
}
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
1.58k
xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1225
1.58k
    SKIP_BLANKS;
1226
1.58k
    if (CUR == '/') {
1227
45
        ctxt->comp->flags |= PAT_FROM_ROOT;
1228
1.53k
    } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1229
1.53k
        ctxt->comp->flags |= PAT_FROM_CUR;
1230
1.53k
    }
1231
1232
1.58k
    if ((CUR == '/') && (NXT(1) == '/')) {
1233
6
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1234
6
  NEXT;
1235
6
  NEXT;
1236
1.57k
    } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1237
0
  PUSH(XML_OP_ANCESTOR, NULL, NULL);
1238
0
  NEXT;
1239
0
  NEXT;
1240
0
  NEXT;
1241
  /* Check for incompleteness. */
1242
0
  SKIP_BLANKS;
1243
0
  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
0
    }
1250
1.58k
    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
1.58k
    } else {
1261
1.58k
        if (CUR == '/') {
1262
39
      PUSH(XML_OP_ROOT, NULL, NULL);
1263
39
      NEXT;
1264
      /* Check for incompleteness. */
1265
39
      SKIP_BLANKS;
1266
39
      if (CUR == 0) {
1267
6
    ERROR5(NULL, NULL, NULL,
1268
6
        "Incomplete expression '%s'.\n", ctxt->base);
1269
6
    ctxt->error = 1;
1270
6
    goto error;
1271
6
      }
1272
39
  }
1273
1.57k
  xmlCompileStepPattern(ctxt);
1274
1.57k
  if (ctxt->error != 0)
1275
846
      goto error;
1276
728
  SKIP_BLANKS;
1277
8.69M
  while (CUR == '/') {
1278
8.69M
      if (NXT(1) == '/') {
1279
355
          PUSH(XML_OP_ANCESTOR, NULL, NULL);
1280
355
    NEXT;
1281
355
    NEXT;
1282
355
    SKIP_BLANKS;
1283
355
    xmlCompileStepPattern(ctxt);
1284
355
    if (ctxt->error != 0)
1285
0
        goto error;
1286
8.69M
      } else {
1287
8.69M
          PUSH(XML_OP_PARENT, NULL, NULL);
1288
8.69M
    NEXT;
1289
8.69M
    SKIP_BLANKS;
1290
8.69M
    if (CUR == 0) {
1291
0
        ERROR5(NULL, NULL, NULL,
1292
0
        "Incomplete expression '%s'.\n", ctxt->base);
1293
0
        ctxt->error = 1;
1294
0
        goto error;
1295
0
    }
1296
8.69M
    xmlCompileStepPattern(ctxt);
1297
8.69M
    if (ctxt->error != 0)
1298
2
        goto error;
1299
8.69M
      }
1300
8.69M
  }
1301
728
    }
1302
726
    if (CUR != 0) {
1303
295
  ERROR5(NULL, NULL, NULL,
1304
295
         "Failed to compile pattern %s\n", ctxt->base);
1305
295
  ctxt->error = 1;
1306
295
    }
1307
1.58k
error:
1308
1.58k
    return;
1309
726
}
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
431
xmlNewStreamComp(int size) {
1484
431
    xmlStreamCompPtr cur;
1485
1486
431
    if (size < 4)
1487
330
        size  = 4;
1488
1489
431
    cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1490
431
    if (cur == NULL) {
1491
0
  ERROR(NULL, NULL, NULL,
1492
0
    "xmlNewStreamComp: malloc failed\n");
1493
0
  return(NULL);
1494
0
    }
1495
431
    memset(cur, 0, sizeof(xmlStreamComp));
1496
431
    cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1497
431
    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
431
    cur->nbStep = 0;
1504
431
    cur->maxStep = size;
1505
431
    return(cur);
1506
431
}
1507
1508
/**
1509
 * xmlFreeStreamComp:
1510
 * @comp: the compiled pattern for streaming
1511
 *
1512
 * Free the compiled pattern for streaming
1513
 */
1514
static void
1515
431
xmlFreeStreamComp(xmlStreamCompPtr comp) {
1516
431
    if (comp != NULL) {
1517
431
        if (comp->steps != NULL)
1518
431
      xmlFree(comp->steps);
1519
431
  if (comp->dict != NULL)
1520
0
      xmlDictFree(comp->dict);
1521
431
        xmlFree(comp);
1522
431
    }
1523
431
}
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
8.61M
                     const xmlChar *ns, int nodeType, int flags) {
1539
8.61M
    xmlStreamStepPtr cur;
1540
1541
8.61M
    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
8.61M
    cur = &comp->steps[comp->nbStep++];
1553
8.61M
    cur->flags = flags;
1554
8.61M
    cur->name = name;
1555
8.61M
    cur->ns = ns;
1556
8.61M
    cur->nodeType = nodeType;
1557
8.61M
    return(comp->nbStep - 1);
1558
8.61M
}
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
431
xmlStreamCompile(xmlPatternPtr comp) {
1570
431
    xmlStreamCompPtr stream;
1571
431
    int i, s = 0, root = 0, flags = 0, prevs = -1;
1572
431
    xmlStepOp step;
1573
1574
431
    if ((comp == NULL) || (comp->steps == NULL))
1575
0
        return(-1);
1576
    /*
1577
     * special case for .
1578
     */
1579
431
    if ((comp->nbStep == 1) &&
1580
431
        (comp->steps[0].op == XML_OP_ELEM) &&
1581
431
  (comp->steps[0].value == NULL) &&
1582
431
  (comp->steps[0].value2 == NULL)) {
1583
3
  stream = xmlNewStreamComp(0);
1584
3
  if (stream == NULL)
1585
0
      return(-1);
1586
  /* Note that the stream will have no steps in this case. */
1587
3
  stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1588
3
  comp->stream = stream;
1589
3
  return(0);
1590
3
    }
1591
1592
428
    stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1593
428
    if (stream == NULL)
1594
0
        return(-1);
1595
428
    if (comp->dict != NULL) {
1596
0
        stream->dict = comp->dict;
1597
0
  xmlDictReference(stream->dict);
1598
0
    }
1599
1600
428
    i = 0;
1601
428
    if (comp->flags & PAT_FROM_ROOT)
1602
19
  stream->flags |= XML_STREAM_FROM_ROOT;
1603
1604
17.2M
    for (;i < comp->nbStep;i++) {
1605
17.2M
  step = comp->steps[i];
1606
17.2M
        switch (step.op) {
1607
0
      case XML_OP_END:
1608
0
          break;
1609
14
      case XML_OP_ROOT:
1610
14
          if (i != 0)
1611
0
        goto error;
1612
14
    root = 1;
1613
14
    break;
1614
0
      case XML_OP_NS:
1615
0
    s = xmlStreamCompAddStep(stream, NULL, step.value,
1616
0
        XML_ELEMENT_NODE, flags);
1617
0
    if (s < 0)
1618
0
        goto error;
1619
0
    prevs = s;
1620
0
    flags = 0;
1621
0
    break;
1622
0
      case XML_OP_ATTR:
1623
0
    flags |= XML_STREAM_STEP_ATTR;
1624
0
    prevs = -1;
1625
0
    s = xmlStreamCompAddStep(stream,
1626
0
        step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1627
0
    flags = 0;
1628
0
    if (s < 0)
1629
0
        goto error;
1630
0
    break;
1631
5.75M
      case XML_OP_ELEM:
1632
5.75M
          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
337
        if ((comp->nbStep == i + 1) &&
1642
337
      (flags & XML_STREAM_STEP_DESC)) {
1643
      /*
1644
      * Mark the special case where the expression resolves
1645
      * to any type of node.
1646
      */
1647
1
      if (comp->nbStep == i + 1) {
1648
1
          stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1649
1
      }
1650
1
      flags |= XML_STREAM_STEP_NODE;
1651
1
      s = xmlStreamCompAddStep(stream, NULL, NULL,
1652
1
          XML_STREAM_ANY_NODE, flags);
1653
1
      if (s < 0)
1654
0
          goto error;
1655
1
      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
1
      if (prevs != -1) {
1663
0
          stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1664
0
          prevs = -1;
1665
0
      }
1666
1
      break;
1667
1668
336
        } else {
1669
      /* Just skip this one. */
1670
336
      continue;
1671
336
        }
1672
337
    }
1673
    /* An element node. */
1674
5.75M
          s = xmlStreamCompAddStep(stream, step.value, step.value2,
1675
5.75M
        XML_ELEMENT_NODE, flags);
1676
5.75M
    if (s < 0)
1677
0
        goto error;
1678
5.75M
    prevs = s;
1679
5.75M
    flags = 0;
1680
5.75M
    break;
1681
0
      case XML_OP_CHILD:
1682
    /* An element node child. */
1683
0
          s = xmlStreamCompAddStep(stream, step.value, step.value2,
1684
0
        XML_ELEMENT_NODE, flags);
1685
0
    if (s < 0)
1686
0
        goto error;
1687
0
    prevs = s;
1688
0
    flags = 0;
1689
0
    break;
1690
2.85M
      case XML_OP_ALL:
1691
2.85M
          s = xmlStreamCompAddStep(stream, NULL, NULL,
1692
2.85M
        XML_ELEMENT_NODE, flags);
1693
2.85M
    if (s < 0)
1694
0
        goto error;
1695
2.85M
    prevs = s;
1696
2.85M
    flags = 0;
1697
2.85M
    break;
1698
8.61M
      case XML_OP_PARENT:
1699
8.61M
          break;
1700
354
      case XML_OP_ANCESTOR:
1701
    /* Skip redundant continuations. */
1702
354
    if (flags & XML_STREAM_STEP_DESC)
1703
0
        break;
1704
354
          flags |= XML_STREAM_STEP_DESC;
1705
    /*
1706
    * Mark the expression as having "//".
1707
    */
1708
354
    if ((stream->flags & XML_STREAM_DESC) == 0)
1709
67
        stream->flags |= XML_STREAM_DESC;
1710
354
    break;
1711
17.2M
  }
1712
17.2M
    }
1713
428
    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
428
    if (stream->nbStep <= s)
1728
0
  goto error;
1729
428
    stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1730
428
    if (root)
1731
14
  stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1732
#ifdef DEBUG_STREAMING
1733
    xmlDebugStreamComp(stream);
1734
#endif
1735
428
    comp->stream = stream;
1736
428
    return(0);
1737
0
error:
1738
0
    xmlFreeStreamComp(stream);
1739
0
    return(0);
1740
428
}
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
1.91k
xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1752
1.91k
    xmlStreamCtxtPtr cur;
1753
1754
1.91k
    cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1755
1.91k
    if (cur == NULL) {
1756
0
  ERROR(NULL, NULL, NULL,
1757
0
    "xmlNewStreamCtxt: malloc failed\n");
1758
0
  return(NULL);
1759
0
    }
1760
1.91k
    memset(cur, 0, sizeof(xmlStreamCtxt));
1761
1.91k
    cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1762
1.91k
    if (cur->states == NULL) {
1763
1
  xmlFree(cur);
1764
1
  ERROR(NULL, NULL, NULL,
1765
1
        "xmlNewStreamCtxt: malloc failed\n");
1766
1
  return(NULL);
1767
1
    }
1768
1.91k
    cur->nbState = 0;
1769
1.91k
    cur->maxState = 4;
1770
1.91k
    cur->level = 0;
1771
1.91k
    cur->comp = stream;
1772
1.91k
    cur->blockLevel = -1;
1773
1.91k
    return(cur);
1774
1.91k
}
1775
1776
/**
1777
 * xmlFreeStreamCtxt:
1778
 * @stream: the stream context
1779
 *
1780
 * Free the stream context
1781
 */
1782
void
1783
1.91k
xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1784
1.91k
    xmlStreamCtxtPtr next;
1785
1786
3.82k
    while (stream != NULL) {
1787
1.91k
        next = stream->next;
1788
1.91k
        if (stream->states != NULL)
1789
1.91k
      xmlFree(stream->states);
1790
1.91k
        xmlFree(stream);
1791
1.91k
  stream = next;
1792
1.91k
    }
1793
1.91k
}
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
145
xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1806
145
    int i;
1807
197
    for (i = 0;i < comp->nbState;i++) {
1808
52
        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
52
    }
1814
145
    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
145
    comp->states[2 * comp->nbState] = idx;
1828
145
    comp->states[2 * comp->nbState++ + 1] = level;
1829
145
    return(comp->nbState - 1);
1830
145
}
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
95.1k
          int nodeType) {
1852
95.1k
    int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1853
95.1k
    xmlStreamCompPtr comp;
1854
95.1k
    xmlStreamStep step;
1855
#ifdef DEBUG_STREAMING
1856
    xmlStreamCtxtPtr orig = stream;
1857
#endif
1858
1859
95.1k
    if ((stream == NULL) || (stream->nbState < 0))
1860
0
        return(-1);
1861
1862
190k
    while (stream != NULL) {
1863
95.1k
  comp = stream->comp;
1864
1865
95.1k
  if ((nodeType == XML_ELEMENT_NODE) &&
1866
95.1k
      (name == NULL) && (ns == NULL)) {
1867
      /* We have a document node here (or a reset). */
1868
74
      stream->nbState = 0;
1869
74
      stream->level = 0;
1870
74
      stream->blockLevel = -1;
1871
74
      if (comp->flags & XML_STREAM_FROM_ROOT) {
1872
74
    if (comp->nbStep == 0) {
1873
        /* TODO: We have a "/." here? */
1874
0
        ret = 1;
1875
74
    } else {
1876
74
        if ((comp->nbStep == 1) &&
1877
74
      (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1878
74
      (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1879
57
        {
1880
      /*
1881
      * In the case of "//." the document node will match
1882
      * as well.
1883
      */
1884
57
      ret = 1;
1885
57
        } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1886
      /* TODO: Do we need this ? */
1887
13
      tmp = xmlStreamCtxtAddState(stream, 0, 0);
1888
13
      if (tmp < 0)
1889
0
          err++;
1890
13
        }
1891
74
    }
1892
74
      }
1893
74
      stream = stream->next;
1894
74
      continue; /* while */
1895
74
  }
1896
1897
  /*
1898
  * Fast check for ".".
1899
  */
1900
95.0k
  if (comp->nbStep == 0) {
1901
      /*
1902
       * / and . are handled at the XPath node set creation
1903
       * level by checking min depth
1904
       */
1905
0
      if (stream->flags & XML_PATTERN_XPATH) {
1906
0
    stream = stream->next;
1907
0
    continue; /* while */
1908
0
      }
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
0
  }
1922
95.0k
  if (stream->blockLevel != -1) {
1923
      /*
1924
      * Skip blocked expressions.
1925
      */
1926
852
      stream->level++;
1927
852
      goto stream_next;
1928
852
  }
1929
1930
94.1k
  if ((nodeType != XML_ELEMENT_NODE) &&
1931
94.1k
      (nodeType != XML_ATTRIBUTE_NODE) &&
1932
94.1k
      ((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
0
      stream->level++;
1939
0
      goto stream_next;
1940
0
  }
1941
1942
  /*
1943
   * Check evolution of existing states
1944
   */
1945
94.1k
  i = 0;
1946
94.1k
  m = stream->nbState;
1947
94.5k
  while (i < m) {
1948
349
      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
131
    stepNr = stream->states[2 * (stream->nbState -1)];
1954
    /*
1955
    * TODO: Security check, should not happen, remove it.
1956
    */
1957
131
    if (stream->states[(2 * (stream->nbState -1)) + 1] <
1958
131
        stream->level) {
1959
0
        return (-1);
1960
0
    }
1961
131
    desc = 0;
1962
    /* loop-stopper */
1963
131
    i = m;
1964
218
      } 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
218
    stepNr = stream->states[2 * i];
1971
1972
    /* TODO: should not happen anymore: dead states */
1973
218
    if (stepNr < 0)
1974
0
        goto next_state;
1975
1976
218
    tmp = stream->states[(2 * i) + 1];
1977
1978
    /* skip new states just added */
1979
218
    if (tmp > stream->level)
1980
0
        goto next_state;
1981
1982
    /* skip states at ancestor levels, except if "//" */
1983
218
    desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1984
218
    if ((tmp < stream->level) && (!desc))
1985
107
        goto next_state;
1986
218
      }
1987
      /*
1988
      * Check for correct node-type.
1989
      */
1990
242
      step = comp->steps[stepNr];
1991
242
      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
242
      match = 0;
2006
242
      if (step.nodeType == XML_STREAM_ANY_NODE) {
2007
0
    match = 1;
2008
242
      } else if (step.name == NULL) {
2009
30
    if (step.ns == NULL) {
2010
        /*
2011
        * This lets through all elements/attributes.
2012
        */
2013
30
        match = 1;
2014
30
    } else if (ns != NULL)
2015
0
        match = xmlStrEqual(step.ns, ns);
2016
212
      } else if (((step.ns != NULL) == (ns != NULL)) &&
2017
212
    (name != NULL) &&
2018
212
    (step.name[0] == name[0]) &&
2019
212
    xmlStrEqual(step.name, name) &&
2020
212
    ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2021
12
      {
2022
12
    match = 1;
2023
12
      }
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
242
      if (match) {
2041
42
    final = step.flags & XML_STREAM_STEP_FINAL;
2042
42
                if (final) {
2043
1
                    ret = 1;
2044
41
                } else {
2045
41
                    xmlStreamCtxtAddState(stream, stepNr + 1,
2046
41
                                          stream->level + 1);
2047
41
                }
2048
42
    if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2049
        /*
2050
        * Check if we have a special case like "foo/bar//.", where
2051
        * "foo" is selected as well.
2052
        */
2053
0
        ret = 1;
2054
0
    }
2055
42
      }
2056
242
      if (((comp->flags & XML_STREAM_DESC) == 0) &&
2057
242
    ((! match) || final))  {
2058
    /*
2059
    * Mark this expression as blocked for any evaluation at
2060
    * deeper levels. Note that this includes "/foo"
2061
    * expressions if the *pattern* behaviour is used.
2062
    */
2063
108
    stream->blockLevel = stream->level +1;
2064
108
      }
2065
349
next_state:
2066
349
      i++;
2067
349
  }
2068
2069
94.1k
  stream->level++;
2070
2071
  /*
2072
  * Re/enter the expression.
2073
  * Don't reenter if it's an absolute expression like "/foo",
2074
  *   except "//foo".
2075
  */
2076
94.1k
  step = comp->steps[0];
2077
94.1k
  if (step.flags & XML_STREAM_STEP_ROOT)
2078
89
      goto stream_next;
2079
2080
94.1k
  desc = step.flags & XML_STREAM_STEP_DESC;
2081
94.1k
  if (stream->flags & XML_PATTERN_NOTPATTERN) {
2082
      /*
2083
      * Re/enter the expression if it is a "descendant" one,
2084
      * or if we are at the 1st level of evaluation.
2085
      */
2086
2087
94.1k
      if (stream->level == 1) {
2088
89.9k
    if (XML_STREAM_XS_IDC(stream)) {
2089
        /*
2090
        * XS-IDC: The missing "self::node()" will always
2091
        * match the first given node.
2092
        */
2093
0
        goto stream_next;
2094
0
    } else
2095
89.9k
        goto compare;
2096
89.9k
      }
2097
      /*
2098
      * A "//" is always reentrant.
2099
      */
2100
4.13k
      if (desc)
2101
3.91k
    goto compare;
2102
2103
      /*
2104
      * XS-IDC: Process the 2nd level, since the missing
2105
      * "self::node()" is responsible for the 2nd level being
2106
      * the real start level.
2107
      */
2108
216
      if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2109
0
    goto compare;
2110
2111
216
      goto stream_next;
2112
216
  }
2113
2114
93.8k
compare:
2115
  /*
2116
  * Check expected node-type.
2117
  */
2118
93.8k
  if (step.nodeType != nodeType) {
2119
3.19k
      if (nodeType == XML_ATTRIBUTE_NODE)
2120
0
    goto stream_next;
2121
3.19k
      else if (step.nodeType != XML_STREAM_ANY_NODE)
2122
0
    goto stream_next;
2123
3.19k
  }
2124
  /*
2125
  * Compare local/namespace-name.
2126
  */
2127
93.8k
  match = 0;
2128
93.8k
  if (step.nodeType == XML_STREAM_ANY_NODE) {
2129
3.19k
      match = 1;
2130
90.6k
  } else if (step.name == NULL) {
2131
71
      if (step.ns == NULL) {
2132
    /*
2133
    * This lets through all elements/attributes.
2134
    */
2135
71
    match = 1;
2136
71
      } else if (ns != NULL)
2137
0
    match = xmlStrEqual(step.ns, ns);
2138
90.6k
  } else if (((step.ns != NULL) == (ns != NULL)) &&
2139
90.6k
      (name != NULL) &&
2140
90.6k
      (step.name[0] == name[0]) &&
2141
90.6k
      xmlStrEqual(step.name, name) &&
2142
90.6k
      ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2143
28
  {
2144
28
      match = 1;
2145
28
  }
2146
93.8k
  final = step.flags & XML_STREAM_STEP_FINAL;
2147
93.8k
  if (match) {
2148
3.29k
      if (final)
2149
3.20k
    ret = 1;
2150
91
      else
2151
91
    xmlStreamCtxtAddState(stream, 1, stream->level);
2152
3.29k
      if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2153
    /*
2154
    * Check if we have a special case like "foo//.", where
2155
    * "foo" is selected as well.
2156
    */
2157
0
    ret = 1;
2158
0
      }
2159
3.29k
  }
2160
93.8k
  if (((comp->flags & XML_STREAM_DESC) == 0) &&
2161
93.8k
      ((! match) || final))  {
2162
      /*
2163
      * Mark this expression as blocked for any evaluation at
2164
      * deeper levels.
2165
      */
2166
89.8k
      stream->blockLevel = stream->level;
2167
89.8k
  }
2168
2169
95.0k
stream_next:
2170
95.0k
        stream = stream->next;
2171
95.0k
    } /* while stream != NULL */
2172
2173
95.1k
    if (err > 0)
2174
0
        ret = -1;
2175
#ifdef DEBUG_STREAMING
2176
    xmlDebugStreamCtxt(orig, ret);
2177
#endif
2178
95.1k
    return(ret);
2179
95.1k
}
2180
2181
/**
2182
 * xmlStreamPush:
2183
 * @stream: the stream context
2184
 * @name: the current name
2185
 * @ns: the namespace name
2186
 *
2187
 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2188
 * indicated a dictionary, then strings for name and ns will be expected
2189
 * to come from the dictionary.
2190
 * Both @name and @ns being NULL means the / i.e. the root of the document.
2191
 * This can also act as a reset.
2192
 * Otherwise the function will act as if it has been given an element-node.
2193
 *
2194
 * Returns: -1 in case of error, 1 if the current state in the stream is a
2195
 *    match and 0 otherwise.
2196
 */
2197
int
2198
xmlStreamPush(xmlStreamCtxtPtr stream,
2199
94.2k
              const xmlChar *name, const xmlChar *ns) {
2200
94.2k
    return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
2201
94.2k
}
2202
2203
/**
2204
 * xmlStreamPushNode:
2205
 * @stream: the stream context
2206
 * @name: the current name
2207
 * @ns: the namespace name
2208
 * @nodeType: the type of the node being pushed
2209
 *
2210
 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2211
 * indicated a dictionary, then strings for name and ns will be expected
2212
 * to come from the dictionary.
2213
 * Both @name and @ns being NULL means the / i.e. the root of the document.
2214
 * This can also act as a reset.
2215
 * Different from xmlStreamPush() this function can be fed with nodes of type:
2216
 * element-, attribute-, text-, cdata-section-, comment- and
2217
 * processing-instruction-node.
2218
 *
2219
 * Returns: -1 in case of error, 1 if the current state in the stream is a
2220
 *    match and 0 otherwise.
2221
 */
2222
int
2223
xmlStreamPushNode(xmlStreamCtxtPtr stream,
2224
      const xmlChar *name, const xmlChar *ns,
2225
      int nodeType)
2226
912
{
2227
912
    return (xmlStreamPushInternal(stream, name, ns,
2228
912
  nodeType));
2229
912
}
2230
2231
/**
2232
* xmlStreamPushAttr:
2233
* @stream: the stream context
2234
* @name: the current name
2235
* @ns: the namespace name
2236
*
2237
* Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2238
* indicated a dictionary, then strings for name and ns will be expected
2239
* to come from the dictionary.
2240
* Both @name and @ns being NULL means the / i.e. the root of the document.
2241
* This can also act as a reset.
2242
* Otherwise the function will act as if it has been given an attribute-node.
2243
*
2244
* Returns: -1 in case of error, 1 if the current state in the stream is a
2245
*    match and 0 otherwise.
2246
*/
2247
int
2248
xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2249
0
      const xmlChar *name, const xmlChar *ns) {
2250
0
    return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
2251
0
}
2252
2253
/**
2254
 * xmlStreamPop:
2255
 * @stream: the stream context
2256
 *
2257
 * push one level from the stream.
2258
 *
2259
 * Returns: -1 in case of error, 0 otherwise.
2260
 */
2261
int
2262
95.0k
xmlStreamPop(xmlStreamCtxtPtr stream) {
2263
95.0k
    int i, lev;
2264
2265
95.0k
    if (stream == NULL)
2266
0
        return(-1);
2267
190k
    while (stream != NULL) {
2268
  /*
2269
  * Reset block-level.
2270
  */
2271
95.0k
  if (stream->blockLevel == stream->level)
2272
89.9k
      stream->blockLevel = -1;
2273
2274
  /*
2275
   *  stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2276
   *  (see the thread at
2277
   *  http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2278
   */
2279
95.0k
  if (stream->level)
2280
95.0k
      stream->level--;
2281
  /*
2282
   * Check evolution of existing states
2283
   */
2284
95.1k
  for (i = stream->nbState -1; i >= 0; i--) {
2285
      /* discard obsoleted states */
2286
1.28k
      lev = stream->states[(2 * i) + 1];
2287
1.28k
      if (lev > stream->level)
2288
132
    stream->nbState--;
2289
1.28k
      if (lev <= stream->level)
2290
1.15k
    break;
2291
1.28k
  }
2292
95.0k
  stream = stream->next;
2293
95.0k
    }
2294
95.0k
    return(0);
2295
95.0k
}
2296
2297
/**
2298
 * xmlStreamWantsAnyNode:
2299
 * @streamCtxt: the stream context
2300
 *
2301
 * Query if the streaming pattern additionally needs to be fed with
2302
 * text-, cdata-section-, comment- and processing-instruction-nodes.
2303
 * If the result is 0 then only element-nodes and attribute-nodes
2304
 * need to be pushed.
2305
 *
2306
 * Returns: 1 in case of need of nodes of the above described types,
2307
 *          0 otherwise. -1 on API errors.
2308
 */
2309
int
2310
xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2311
1.91k
{
2312
1.91k
    if (streamCtxt == NULL)
2313
0
  return(-1);
2314
3.76k
    while (streamCtxt != NULL) {
2315
1.91k
  if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2316
57
      return(1);
2317
1.85k
  streamCtxt = streamCtxt->next;
2318
1.85k
    }
2319
1.85k
    return(0);
2320
1.91k
}
2321
2322
/************************************************************************
2323
 *                  *
2324
 *      The public interfaces       *
2325
 *                  *
2326
 ************************************************************************/
2327
2328
/**
2329
 * xmlPatterncompile:
2330
 * @pattern: the pattern to compile
2331
 * @dict: an optional dictionary for interned strings
2332
 * @flags: compilation flags, see xmlPatternFlags
2333
 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2334
 *
2335
 * Compile a pattern.
2336
 *
2337
 * Returns the compiled form of the pattern or NULL in case of error
2338
 */
2339
xmlPatternPtr
2340
xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2341
1.61k
                  const xmlChar **namespaces) {
2342
1.61k
    xmlPatternPtr ret = NULL, cur;
2343
1.61k
    xmlPatParserContextPtr ctxt = NULL;
2344
1.61k
    const xmlChar *or, *start;
2345
1.61k
    xmlChar *tmp = NULL;
2346
1.61k
    int type = 0;
2347
1.61k
    int streamable = 1;
2348
2349
1.61k
    if (pattern == NULL)
2350
0
        return(NULL);
2351
2352
1.61k
    start = pattern;
2353
1.61k
    or = start;
2354
2.04k
    while (*or != 0) {
2355
1.58k
  tmp = NULL;
2356
26.0M
  while ((*or != 0) && (*or != '|')) or++;
2357
1.58k
        if (*or == 0)
2358
1.53k
      ctxt = xmlNewPatParserContext(start, dict, namespaces);
2359
41
  else {
2360
41
      tmp = xmlStrndup(start, or - start);
2361
41
      if (tmp != NULL) {
2362
41
    ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2363
41
      }
2364
41
      or++;
2365
41
  }
2366
1.58k
  if (ctxt == NULL) goto error;
2367
1.58k
  cur = xmlNewPattern();
2368
1.58k
  if (cur == NULL) goto error;
2369
  /*
2370
  * Assign string dict.
2371
  */
2372
1.58k
  if (dict) {
2373
0
      cur->dict = dict;
2374
0
      xmlDictReference(dict);
2375
0
  }
2376
1.58k
  if (ret == NULL)
2377
1.56k
      ret = cur;
2378
18
  else {
2379
18
      cur->next = ret->next;
2380
18
      ret->next = cur;
2381
18
  }
2382
1.58k
  cur->flags = flags;
2383
1.58k
  ctxt->comp = cur;
2384
2385
1.58k
  if (XML_STREAM_XS_IDC(cur))
2386
0
      xmlCompileIDCXPathPath(ctxt);
2387
1.58k
  else
2388
1.58k
      xmlCompilePathPattern(ctxt);
2389
1.58k
  if (ctxt->error != 0)
2390
1.14k
      goto error;
2391
431
  xmlFreePatParserContext(ctxt);
2392
431
  ctxt = NULL;
2393
2394
2395
431
        if (streamable) {
2396
431
      if (type == 0) {
2397
431
          type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2398
431
      } else if (type == PAT_FROM_ROOT) {
2399
0
          if (cur->flags & PAT_FROM_CUR)
2400
0
        streamable = 0;
2401
0
      } else if (type == PAT_FROM_CUR) {
2402
0
          if (cur->flags & PAT_FROM_ROOT)
2403
0
        streamable = 0;
2404
0
      }
2405
431
  }
2406
431
  if (streamable)
2407
431
      xmlStreamCompile(cur);
2408
431
  if (xmlReversePattern(cur) < 0)
2409
0
      goto error;
2410
431
  if (tmp != NULL) {
2411
19
      xmlFree(tmp);
2412
19
      tmp = NULL;
2413
19
  }
2414
431
  start = or;
2415
431
    }
2416
468
    if (streamable == 0) {
2417
0
        cur = ret;
2418
0
  while (cur != NULL) {
2419
0
      if (cur->stream != NULL) {
2420
0
    xmlFreeStreamComp(cur->stream);
2421
0
    cur->stream = NULL;
2422
0
      }
2423
0
      cur = cur->next;
2424
0
  }
2425
0
    }
2426
2427
468
    return(ret);
2428
1.14k
error:
2429
1.14k
    if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2430
1.14k
    if (ret != NULL) xmlFreePattern(ret);
2431
1.14k
    if (tmp != NULL) xmlFree(tmp);
2432
1.14k
    return(NULL);
2433
1.61k
}
2434
2435
/**
2436
 * xmlPatternMatch:
2437
 * @comp: the precompiled pattern
2438
 * @node: a node
2439
 *
2440
 * Test whether the node matches the pattern
2441
 *
2442
 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2443
 */
2444
int
2445
xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2446
0
{
2447
0
    int ret = 0;
2448
2449
0
    if ((comp == NULL) || (node == NULL))
2450
0
        return(-1);
2451
2452
0
    while (comp != NULL) {
2453
0
        ret = xmlPatMatch(comp, node);
2454
0
  if (ret != 0)
2455
0
      return(ret);
2456
0
  comp = comp->next;
2457
0
    }
2458
0
    return(ret);
2459
0
}
2460
2461
/**
2462
 * xmlPatternGetStreamCtxt:
2463
 * @comp: the precompiled pattern
2464
 *
2465
 * Get a streaming context for that pattern
2466
 * Use xmlFreeStreamCtxt to free the context.
2467
 *
2468
 * Returns a pointer to the context or NULL in case of failure
2469
 */
2470
xmlStreamCtxtPtr
2471
xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2472
1.91k
{
2473
1.91k
    xmlStreamCtxtPtr ret = NULL, cur;
2474
2475
1.91k
    if ((comp == NULL) || (comp->stream == NULL))
2476
0
        return(NULL);
2477
2478
3.82k
    while (comp != NULL) {
2479
1.91k
        if (comp->stream == NULL)
2480
0
      goto failed;
2481
1.91k
  cur = xmlNewStreamCtxt(comp->stream);
2482
1.91k
  if (cur == NULL)
2483
1
      goto failed;
2484
1.91k
  if (ret == NULL)
2485
1.91k
      ret = cur;
2486
0
  else {
2487
0
      cur->next = ret->next;
2488
0
      ret->next = cur;
2489
0
  }
2490
1.91k
  cur->flags = comp->flags;
2491
1.91k
  comp = comp->next;
2492
1.91k
    }
2493
1.91k
    return(ret);
2494
1
failed:
2495
1
    xmlFreeStreamCtxt(ret);
2496
1
    return(NULL);
2497
1.91k
}
2498
2499
/**
2500
 * xmlPatternStreamable:
2501
 * @comp: the precompiled pattern
2502
 *
2503
 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2504
 * should work.
2505
 *
2506
 * Returns 1 if streamable, 0 if not and -1 in case of error.
2507
 */
2508
int
2509
413
xmlPatternStreamable(xmlPatternPtr comp) {
2510
413
    if (comp == NULL)
2511
0
        return(-1);
2512
826
    while (comp != NULL) {
2513
413
        if (comp->stream == NULL)
2514
0
      return(0);
2515
413
  comp = comp->next;
2516
413
    }
2517
413
    return(1);
2518
413
}
2519
2520
/**
2521
 * xmlPatternMaxDepth:
2522
 * @comp: the precompiled pattern
2523
 *
2524
 * Check the maximum depth reachable by a pattern
2525
 *
2526
 * Returns -2 if no limit (using //), otherwise the depth,
2527
 *         and -1 in case of error
2528
 */
2529
int
2530
1.94k
xmlPatternMaxDepth(xmlPatternPtr comp) {
2531
1.94k
    int ret = 0, i;
2532
1.94k
    if (comp == NULL)
2533
0
        return(-1);
2534
3.76k
    while (comp != NULL) {
2535
1.94k
        if (comp->stream == NULL)
2536
0
      return(-1);
2537
1.12M
  for (i = 0;i < comp->stream->nbStep;i++)
2538
1.12M
      if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2539
123
          return(-2);
2540
1.81k
  if (comp->stream->nbStep > ret)
2541
1.81k
      ret = comp->stream->nbStep;
2542
1.81k
  comp = comp->next;
2543
1.81k
    }
2544
1.81k
    return(ret);
2545
1.94k
}
2546
2547
/**
2548
 * xmlPatternMinDepth:
2549
 * @comp: the precompiled pattern
2550
 *
2551
 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2552
 * part of the set.
2553
 *
2554
 * Returns -1 in case of error otherwise the depth,
2555
 *
2556
 */
2557
int
2558
1.94k
xmlPatternMinDepth(xmlPatternPtr comp) {
2559
1.94k
    int ret = 12345678;
2560
1.94k
    if (comp == NULL)
2561
0
        return(-1);
2562
3.88k
    while (comp != NULL) {
2563
1.94k
        if (comp->stream == NULL)
2564
0
      return(-1);
2565
1.94k
  if (comp->stream->nbStep < ret)
2566
1.94k
      ret = comp->stream->nbStep;
2567
1.94k
  if (ret == 0)
2568
3
      return(0);
2569
1.93k
  comp = comp->next;
2570
1.93k
    }
2571
1.93k
    return(ret);
2572
1.94k
}
2573
2574
/**
2575
 * xmlPatternFromRoot:
2576
 * @comp: the precompiled pattern
2577
 *
2578
 * Check if the pattern must be looked at from the root.
2579
 *
2580
 * Returns 1 if true, 0 if false and -1 in case of error
2581
 */
2582
int
2583
1.94k
xmlPatternFromRoot(xmlPatternPtr comp) {
2584
1.94k
    if (comp == NULL)
2585
0
        return(-1);
2586
3.81k
    while (comp != NULL) {
2587
1.94k
        if (comp->stream == NULL)
2588
0
      return(-1);
2589
1.94k
  if (comp->flags & PAT_FROM_ROOT)
2590
74
      return(1);
2591
1.86k
  comp = comp->next;
2592
1.86k
    }
2593
1.86k
    return(0);
2594
2595
1.94k
}
2596
2597
#endif /* LIBXML_PATTERN_ENABLED */