Coverage Report

Created: 2024-09-06 07:53

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