Coverage Report

Created: 2025-07-07 10:01

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