Coverage Report

Created: 2023-09-28 22:19

/src/libxslt/libxslt/xsltutils.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * xsltutils.c: Utilities for the XSL Transformation 1.0 engine
3
 *
4
 * Reference:
5
 *   http://www.w3.org/TR/1999/REC-xslt-19991116
6
 *
7
 * See Copyright for the status of this software.
8
 *
9
 * daniel@veillard.com
10
 */
11
12
#define IN_LIBXSLT
13
#include "libxslt.h"
14
15
#ifndef XSLT_NEED_TRIO
16
#include <stdio.h>
17
#else
18
#include <trio.h>
19
#endif
20
21
#include <string.h>
22
#include <stdlib.h>
23
#include <stdarg.h>
24
#include <time.h>
25
26
#ifdef HAVE_SYS_TIME_H
27
#include <sys/time.h>
28
#endif
29
#ifdef HAVE_UNISTD_H
30
#include <unistd.h>
31
#endif
32
33
#include <libxml/xmlmemory.h>
34
#include <libxml/tree.h>
35
#include <libxml/HTMLtree.h>
36
#include <libxml/xmlerror.h>
37
#include <libxml/xmlIO.h>
38
#include "xsltutils.h"
39
#include "templates.h"
40
#include "xsltInternals.h"
41
#include "imports.h"
42
#include "transform.h"
43
44
#if defined(_WIN32)
45
#include <windows.h>
46
#define XSLT_WIN32_PERFORMANCE_COUNTER
47
#endif
48
49
/************************************************************************
50
 *                  *
51
 *      Convenience function        *
52
 *                  *
53
 ************************************************************************/
54
55
/**
56
 * xsltGetCNsProp:
57
 * @style: the stylesheet
58
 * @node:  the node
59
 * @name:  the attribute name
60
 * @nameSpace:  the URI of the namespace
61
 *
62
 * Similar to xmlGetNsProp() but with a slightly different semantic
63
 *
64
 * Search and get the value of an attribute associated to a node
65
 * This attribute has to be anchored in the namespace specified,
66
 * or has no namespace and the element is in that namespace.
67
 *
68
 * This does the entity substitution.
69
 * This function looks in DTD attribute declaration for #FIXED or
70
 * default declaration values unless DTD use has been turned off.
71
 *
72
 * Returns the attribute value or NULL if not found. The string is allocated
73
 *         in the stylesheet dictionary.
74
 */
75
const xmlChar *
76
xsltGetCNsProp(xsltStylesheetPtr style, xmlNodePtr node,
77
0
              const xmlChar *name, const xmlChar *nameSpace) {
78
0
    xmlAttrPtr prop;
79
0
    xmlDocPtr doc;
80
0
    xmlNsPtr ns;
81
0
    xmlChar *tmp;
82
0
    const xmlChar *ret;
83
84
0
    if ((node == NULL) || (style == NULL) || (style->dict == NULL))
85
0
  return(NULL);
86
87
0
    if (nameSpace == NULL)
88
0
        return xmlGetProp(node, name);
89
90
0
    if (node->type == XML_NAMESPACE_DECL)
91
0
        return(NULL);
92
0
    if (node->type == XML_ELEMENT_NODE)
93
0
  prop = node->properties;
94
0
    else
95
0
  prop = NULL;
96
0
    while (prop != NULL) {
97
  /*
98
   * One need to have
99
   *   - same attribute names
100
   *   - and the attribute carrying that namespace
101
   */
102
0
        if ((xmlStrEqual(prop->name, name)) &&
103
0
      (((prop->ns == NULL) && (node->ns != NULL) &&
104
0
        (xmlStrEqual(node->ns->href, nameSpace))) ||
105
0
       ((prop->ns != NULL) &&
106
0
        (xmlStrEqual(prop->ns->href, nameSpace))))) {
107
108
0
      tmp = xmlNodeListGetString(node->doc, prop->children, 1);
109
0
      if (tmp == NULL)
110
0
          ret = xmlDictLookup(style->dict, BAD_CAST "", 0);
111
0
      else {
112
0
          ret = xmlDictLookup(style->dict, tmp, -1);
113
0
    xmlFree(tmp);
114
0
      }
115
0
      return ret;
116
0
        }
117
0
  prop = prop->next;
118
0
    }
119
0
    tmp = NULL;
120
    /*
121
     * Check if there is a default declaration in the internal
122
     * or external subsets
123
     */
124
0
    doc =  node->doc;
125
0
    if (doc != NULL) {
126
0
        if (doc->intSubset != NULL) {
127
0
      xmlAttributePtr attrDecl;
128
129
0
      attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
130
0
      if ((attrDecl == NULL) && (doc->extSubset != NULL))
131
0
    attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
132
133
0
      if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
134
          /*
135
     * The DTD declaration only allows a prefix search
136
     */
137
0
    ns = xmlSearchNs(doc, node, attrDecl->prefix);
138
0
    if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
139
0
        return(xmlDictLookup(style->dict,
140
0
                             attrDecl->defaultValue, -1));
141
0
      }
142
0
  }
143
0
    }
144
0
    return(NULL);
145
0
}
146
/**
147
 * xsltGetNsProp:
148
 * @node:  the node
149
 * @name:  the attribute name
150
 * @nameSpace:  the URI of the namespace
151
 *
152
 * Similar to xmlGetNsProp() but with a slightly different semantic
153
 *
154
 * Search and get the value of an attribute associated to a node
155
 * This attribute has to be anchored in the namespace specified,
156
 * or has no namespace and the element is in that namespace.
157
 *
158
 * This does the entity substitution.
159
 * This function looks in DTD attribute declaration for #FIXED or
160
 * default declaration values unless DTD use has been turned off.
161
 *
162
 * Returns the attribute value or NULL if not found.
163
 *     It's up to the caller to free the memory.
164
 */
165
xmlChar *
166
0
xsltGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) {
167
0
    xmlAttrPtr prop;
168
0
    xmlDocPtr doc;
169
0
    xmlNsPtr ns;
170
171
0
    if (node == NULL)
172
0
  return(NULL);
173
174
0
    if (nameSpace == NULL)
175
0
        return xmlGetProp(node, name);
176
177
0
    if (node->type == XML_NAMESPACE_DECL)
178
0
        return(NULL);
179
0
    if (node->type == XML_ELEMENT_NODE)
180
0
  prop = node->properties;
181
0
    else
182
0
  prop = NULL;
183
    /*
184
    * TODO: Substitute xmlGetProp() for xmlGetNsProp(), since the former
185
    * is not namespace-aware and will return an attribute with equal
186
    * name regardless of its namespace.
187
    * Example:
188
    *   <xsl:element foo:name="myName"/>
189
    *   So this would return "myName" even if an attribute @name
190
    *   in the XSLT was requested.
191
    */
192
0
    while (prop != NULL) {
193
  /*
194
   * One need to have
195
   *   - same attribute names
196
   *   - and the attribute carrying that namespace
197
   */
198
0
        if ((xmlStrEqual(prop->name, name)) &&
199
0
      (((prop->ns == NULL) && (node->ns != NULL) &&
200
0
        (xmlStrEqual(node->ns->href, nameSpace))) ||
201
0
       ((prop->ns != NULL) &&
202
0
        (xmlStrEqual(prop->ns->href, nameSpace))))) {
203
0
      xmlChar *ret;
204
205
0
      ret = xmlNodeListGetString(node->doc, prop->children, 1);
206
0
      if (ret == NULL) return(xmlStrdup((xmlChar *)""));
207
0
      return(ret);
208
0
        }
209
0
  prop = prop->next;
210
0
    }
211
212
    /*
213
     * Check if there is a default declaration in the internal
214
     * or external subsets
215
     */
216
0
    doc =  node->doc;
217
0
    if (doc != NULL) {
218
0
        if (doc->intSubset != NULL) {
219
0
      xmlAttributePtr attrDecl;
220
221
0
      attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name);
222
0
      if ((attrDecl == NULL) && (doc->extSubset != NULL))
223
0
    attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name);
224
225
0
      if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) {
226
          /*
227
     * The DTD declaration only allows a prefix search
228
     */
229
0
    ns = xmlSearchNs(doc, node, attrDecl->prefix);
230
0
    if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace)))
231
0
        return(xmlStrdup(attrDecl->defaultValue));
232
0
      }
233
0
  }
234
0
    }
235
0
    return(NULL);
236
0
}
237
238
/**
239
 * xsltGetUTF8Char:
240
 * @utf:  a sequence of UTF-8 encoded bytes
241
 * @len:  a pointer to @bytes len
242
 *
243
 * Read one UTF8 Char from @utf
244
 * Function copied from libxml2 xmlGetUTF8Char() ... to discard ultimately
245
 * and use the original API
246
 *
247
 * Returns the char value or -1 in case of error and update @len with the
248
 *        number of bytes used
249
 */
250
int
251
97.6k
xsltGetUTF8Char(const unsigned char *utf, int *len) {
252
97.6k
    unsigned int c;
253
254
97.6k
    if (utf == NULL)
255
0
  goto error;
256
97.6k
    if (len == NULL)
257
0
  goto error;
258
97.6k
    if (*len < 1)
259
0
  goto error;
260
261
97.6k
    c = utf[0];
262
97.6k
    if (c & 0x80) {
263
0
  if (*len < 2)
264
0
      goto error;
265
0
  if ((utf[1] & 0xc0) != 0x80)
266
0
      goto error;
267
0
  if ((c & 0xe0) == 0xe0) {
268
0
      if (*len < 3)
269
0
    goto error;
270
0
      if ((utf[2] & 0xc0) != 0x80)
271
0
    goto error;
272
0
      if ((c & 0xf0) == 0xf0) {
273
0
    if (*len < 4)
274
0
        goto error;
275
0
    if ((c & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
276
0
        goto error;
277
0
    *len = 4;
278
    /* 4-byte code */
279
0
    c = (utf[0] & 0x7) << 18;
280
0
    c |= (utf[1] & 0x3f) << 12;
281
0
    c |= (utf[2] & 0x3f) << 6;
282
0
    c |= utf[3] & 0x3f;
283
0
      } else {
284
        /* 3-byte code */
285
0
    *len = 3;
286
0
    c = (utf[0] & 0xf) << 12;
287
0
    c |= (utf[1] & 0x3f) << 6;
288
0
    c |= utf[2] & 0x3f;
289
0
      }
290
0
  } else {
291
    /* 2-byte code */
292
0
      *len = 2;
293
0
      c = (utf[0] & 0x1f) << 6;
294
0
      c |= utf[1] & 0x3f;
295
0
  }
296
97.6k
    } else {
297
  /* 1-byte code */
298
97.6k
  *len = 1;
299
97.6k
    }
300
97.6k
    return(c);
301
302
0
error:
303
0
    if (len != NULL)
304
0
  *len = 0;
305
0
    return(-1);
306
97.6k
}
307
308
#ifdef XSLT_REFACTORED
309
310
/**
311
 * xsltPointerListAddSize:
312
 * @list: the pointer list structure
313
 * @item: the item to be stored
314
 * @initialSize: the initial size of the list
315
 *
316
 * Adds an item to the list.
317
 *
318
 * Returns the position of the added item in the list or
319
 *         -1 in case of an error.
320
 */
321
int
322
xsltPointerListAddSize(xsltPointerListPtr list,
323
           void *item,
324
           int initialSize)
325
{
326
    if (list->items == NULL) {
327
  if (initialSize <= 0)
328
      initialSize = 1;
329
  list->items = (void **) xmlMalloc(
330
      initialSize * sizeof(void *));
331
  if (list->items == NULL) {
332
      xsltGenericError(xsltGenericErrorContext,
333
       "xsltPointerListAddSize: memory allocation failure.\n");
334
      return(-1);
335
  }
336
  list->number = 0;
337
  list->size = initialSize;
338
    } else if (list->size <= list->number) {
339
  list->size *= 2;
340
  list->items = (void **) xmlRealloc(list->items,
341
      list->size * sizeof(void *));
342
  if (list->items == NULL) {
343
      xsltGenericError(xsltGenericErrorContext,
344
       "xsltPointerListAddSize: memory re-allocation failure.\n");
345
      list->size = 0;
346
      return(-1);
347
  }
348
    }
349
    list->items[list->number++] = item;
350
    return(0);
351
}
352
353
/**
354
 * xsltPointerListCreate:
355
 * @initialSize: the initial size for the list
356
 *
357
 * Creates an xsltPointerList structure.
358
 *
359
 * Returns a xsltPointerList structure or NULL in case of an error.
360
 */
361
xsltPointerListPtr
362
xsltPointerListCreate(int initialSize)
363
{
364
    xsltPointerListPtr ret;
365
366
    ret = xmlMalloc(sizeof(xsltPointerList));
367
    if (ret == NULL) {
368
  xsltGenericError(xsltGenericErrorContext,
369
       "xsltPointerListCreate: memory allocation failure.\n");
370
  return (NULL);
371
    }
372
    memset(ret, 0, sizeof(xsltPointerList));
373
    if (initialSize > 0) {
374
  xsltPointerListAddSize(ret, NULL, initialSize);
375
  ret->number = 0;
376
    }
377
    return (ret);
378
}
379
380
/**
381
 * xsltPointerListFree:
382
 * @list: pointer to the list to be freed
383
 *
384
 * Frees the xsltPointerList structure. This does not free
385
 * the content of the list.
386
 */
387
void
388
xsltPointerListFree(xsltPointerListPtr list)
389
{
390
    if (list == NULL)
391
  return;
392
    if (list->items != NULL)
393
  xmlFree(list->items);
394
    xmlFree(list);
395
}
396
397
/**
398
 * xsltPointerListClear:
399
 * @list: pointer to the list to be cleared
400
 *
401
 * Resets the list, but does not free the allocated array
402
 * and does not free the content of the list.
403
 */
404
void
405
xsltPointerListClear(xsltPointerListPtr list)
406
{
407
    if (list->items != NULL) {
408
  xmlFree(list->items);
409
  list->items = NULL;
410
    }
411
    list->number = 0;
412
    list->size = 0;
413
}
414
415
#endif /* XSLT_REFACTORED */
416
417
/************************************************************************
418
 *                  *
419
 *    Handling of XSLT stylesheets messages     *
420
 *                  *
421
 ************************************************************************/
422
423
/**
424
 * xsltMessage:
425
 * @ctxt:  an XSLT processing context
426
 * @node:  The current node
427
 * @inst:  The node containing the message instruction
428
 *
429
 * Process and xsl:message construct
430
 */
431
void
432
0
xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
433
0
    xmlGenericErrorFunc error = xsltGenericError;
434
0
    void *errctx = xsltGenericErrorContext;
435
0
    xmlChar *prop, *message;
436
0
    int terminate = 0;
437
438
0
    if ((ctxt == NULL) || (inst == NULL))
439
0
  return;
440
441
0
    if (ctxt->error != NULL) {
442
0
  error = ctxt->error;
443
0
  errctx = ctxt->errctx;
444
0
    }
445
446
0
    prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", NULL);
447
0
    if (prop != NULL) {
448
0
  if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
449
0
      terminate = 1;
450
0
  } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
451
0
      terminate = 0;
452
0
  } else {
453
0
      xsltTransformError(ctxt, NULL, inst,
454
0
    "xsl:message : terminate expecting 'yes' or 'no'\n");
455
0
  }
456
0
  xmlFree(prop);
457
0
    }
458
0
    message = xsltEvalTemplateString(ctxt, node, inst);
459
0
    if (message != NULL) {
460
0
  int len = xmlStrlen(message);
461
462
0
  error(errctx, "%s", (const char *)message);
463
0
  if ((len > 0) && (message[len - 1] != '\n'))
464
0
      error(errctx, "\n");
465
0
  xmlFree(message);
466
0
    }
467
0
    if (terminate)
468
0
  ctxt->state = XSLT_STATE_STOPPED;
469
0
}
470
471
/************************************************************************
472
 *                  *
473
 *    Handling of out of context errors     *
474
 *                  *
475
 ************************************************************************/
476
477
1.11M
#define XSLT_GET_VAR_STR(msg, str) {       \
478
1.11M
    int       size;           \
479
1.11M
    int       chars;            \
480
1.11M
    char      *larger;            \
481
1.11M
    va_list   ap;           \
482
1.11M
                \
483
1.11M
    str = (char *) xmlMalloc(150);        \
484
1.11M
    if (str == NULL)           \
485
1.11M
  return;             \
486
1.11M
                \
487
1.11M
    size = 150;             \
488
1.11M
                \
489
1.21M
    while (size < 64000) {         \
490
1.21M
  va_start(ap, msg);          \
491
1.21M
  chars = vsnprintf(str, size, msg, ap);      \
492
1.21M
  va_end(ap);           \
493
1.21M
  if ((chars > -1) && (chars < size))     \
494
1.21M
      break;           \
495
1.21M
  if (chars > -1)           \
496
95.0k
      size += chars + 1;         \
497
95.0k
  else              \
498
95.0k
      size += 100;         \
499
95.0k
  if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\
500
0
      xmlFree(str);         \
501
0
      return;           \
502
0
  }              \
503
95.0k
  str = larger;           \
504
95.0k
    }                \
505
1.11M
}
506
/**
507
 * xsltGenericErrorDefaultFunc:
508
 * @ctx:  an error context
509
 * @msg:  the message to display/transmit
510
 * @...:  extra parameters for the message display
511
 *
512
 * Default handler for out of context error messages.
513
 */
514
static void LIBXSLT_ATTR_FORMAT(2,3)
515
0
xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
516
0
    va_list args;
517
518
0
    if (xsltGenericErrorContext == NULL)
519
0
  xsltGenericErrorContext = (void *) stderr;
520
521
0
    va_start(args, msg);
522
0
    vfprintf((FILE *)xsltGenericErrorContext, msg, args);
523
0
    va_end(args);
524
0
}
525
526
xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
527
void *xsltGenericErrorContext = NULL;
528
529
530
/**
531
 * xsltSetGenericErrorFunc:
532
 * @ctx:  the new error handling context
533
 * @handler:  the new handler function
534
 *
535
 * Function to reset the handler and the error context for out of
536
 * context error messages.
537
 * This simply means that @handler will be called for subsequent
538
 * error messages while not parsing nor validating. And @ctx will
539
 * be passed as first argument to @handler
540
 * One can simply force messages to be emitted to another FILE * than
541
 * stderr by setting @ctx to this file handle and @handler to NULL.
542
 */
543
void
544
3.70k
xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
545
3.70k
    xsltGenericErrorContext = ctx;
546
3.70k
    if (handler != NULL)
547
3.70k
  xsltGenericError = handler;
548
0
    else
549
0
  xsltGenericError = xsltGenericErrorDefaultFunc;
550
3.70k
}
551
552
/**
553
 * xsltGenericDebugDefaultFunc:
554
 * @ctx:  an error context
555
 * @msg:  the message to display/transmit
556
 * @...:  extra parameters for the message display
557
 *
558
 * Default handler for out of context error messages.
559
 */
560
static void LIBXSLT_ATTR_FORMAT(2,3)
561
35.7k
xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
562
35.7k
    va_list args;
563
564
35.7k
    if (xsltGenericDebugContext == NULL)
565
35.7k
  return;
566
567
0
    va_start(args, msg);
568
0
    vfprintf((FILE *)xsltGenericDebugContext, msg, args);
569
0
    va_end(args);
570
0
}
571
572
xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
573
void *xsltGenericDebugContext = NULL;
574
575
576
/**
577
 * xsltSetGenericDebugFunc:
578
 * @ctx:  the new error handling context
579
 * @handler:  the new handler function
580
 *
581
 * Function to reset the handler and the error context for out of
582
 * context error messages.
583
 * This simply means that @handler will be called for subsequent
584
 * error messages while not parsing or validating. And @ctx will
585
 * be passed as first argument to @handler
586
 * One can simply force messages to be emitted to another FILE * than
587
 * stderr by setting @ctx to this file handle and @handler to NULL.
588
 */
589
void
590
0
xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
591
0
    xsltGenericDebugContext = ctx;
592
0
    if (handler != NULL)
593
0
  xsltGenericDebug = handler;
594
0
    else
595
0
  xsltGenericDebug = xsltGenericDebugDefaultFunc;
596
0
}
597
598
/**
599
 * xsltPrintErrorContext:
600
 * @ctxt:  the transformation context
601
 * @style:  the stylesheet
602
 * @node:  the current node being processed
603
 *
604
 * Display the context of an error.
605
 */
606
void
607
xsltPrintErrorContext(xsltTransformContextPtr ctxt,
608
1.11M
                xsltStylesheetPtr style, xmlNodePtr node) {
609
1.11M
    int line = 0;
610
1.11M
    const xmlChar *file = NULL;
611
1.11M
    const xmlChar *name = NULL;
612
1.11M
    const char *type = "error";
613
1.11M
    xmlGenericErrorFunc error = xsltGenericError;
614
1.11M
    void *errctx = xsltGenericErrorContext;
615
616
1.11M
    if (ctxt != NULL) {
617
1.06M
        if (ctxt->state == XSLT_STATE_OK)
618
0
      ctxt->state = XSLT_STATE_ERROR;
619
1.06M
  if (ctxt->error != NULL) {
620
0
      error = ctxt->error;
621
0
      errctx = ctxt->errctx;
622
0
  }
623
1.06M
    }
624
1.11M
    if ((node == NULL) && (ctxt != NULL))
625
20
  node = ctxt->inst;
626
627
1.11M
    if (node != NULL)  {
628
1.06M
  if ((node->type == XML_DOCUMENT_NODE) ||
629
1.06M
      (node->type == XML_HTML_DOCUMENT_NODE)) {
630
0
      xmlDocPtr doc = (xmlDocPtr) node;
631
632
0
      file = doc->URL;
633
1.06M
  } else {
634
1.06M
      line = xmlGetLineNo(node);
635
1.06M
      if ((node->doc != NULL) && (node->doc->URL != NULL))
636
1.06M
    file = node->doc->URL;
637
1.06M
      if (node->name != NULL)
638
1.06M
    name = node->name;
639
1.06M
  }
640
1.06M
    }
641
642
1.11M
    if (ctxt != NULL)
643
1.06M
  type = "runtime error";
644
50.4k
    else if (style != NULL) {
645
#ifdef XSLT_REFACTORED
646
  if (XSLT_CCTXT(style)->errSeverity == XSLT_ERROR_SEVERITY_WARNING)
647
      type = "compilation warning";
648
  else
649
      type = "compilation error";
650
#else
651
0
  type = "compilation error";
652
0
#endif
653
0
    }
654
655
1.11M
    if ((file != NULL) && (line != 0) && (name != NULL))
656
1.06M
  error(errctx, "%s: file %s line %d element %s\n",
657
1.06M
        type, file, line, name);
658
50.4k
    else if ((file != NULL) && (name != NULL))
659
0
  error(errctx, "%s: file %s element %s\n", type, file, name);
660
50.4k
    else if ((file != NULL) && (line != 0))
661
0
  error(errctx, "%s: file %s line %d\n", type, file, line);
662
50.4k
    else if (file != NULL)
663
0
  error(errctx, "%s: file %s\n", type, file);
664
50.4k
    else if (name != NULL)
665
0
  error(errctx, "%s: element %s\n", type, name);
666
50.4k
    else
667
50.4k
  error(errctx, "%s\n", type);
668
1.11M
}
669
670
/**
671
 * xsltSetTransformErrorFunc:
672
 * @ctxt:  the XSLT transformation context
673
 * @ctx:  the new error handling context
674
 * @handler:  the new handler function
675
 *
676
 * Function to reset the handler and the error context for out of
677
 * context error messages specific to a given XSLT transromation.
678
 *
679
 * This simply means that @handler will be called for subsequent
680
 * error messages while running the transformation.
681
 */
682
void
683
xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,
684
                          void *ctx, xmlGenericErrorFunc handler)
685
0
{
686
0
    ctxt->error = handler;
687
0
    ctxt->errctx = ctx;
688
0
}
689
690
/**
691
 * xsltTransformError:
692
 * @ctxt:  an XSLT transformation context
693
 * @style:  the XSLT stylesheet used
694
 * @node:  the current node in the stylesheet
695
 * @msg:  the message to display/transmit
696
 * @...:  extra parameters for the message display
697
 *
698
 * Display and format an error messages, gives file, line, position and
699
 * extra parameters, will use the specific transformation context if available
700
 */
701
void
702
xsltTransformError(xsltTransformContextPtr ctxt,
703
       xsltStylesheetPtr style,
704
       xmlNodePtr node,
705
1.11M
       const char *msg, ...) {
706
1.11M
    xmlGenericErrorFunc error = xsltGenericError;
707
1.11M
    void *errctx = xsltGenericErrorContext;
708
1.11M
    char * str;
709
710
1.11M
    if (ctxt != NULL) {
711
1.06M
        if (ctxt->state == XSLT_STATE_OK)
712
1.78k
      ctxt->state = XSLT_STATE_ERROR;
713
1.06M
  if (ctxt->error != NULL) {
714
0
      error = ctxt->error;
715
0
      errctx = ctxt->errctx;
716
0
  }
717
1.06M
    }
718
1.11M
    if ((node == NULL) && (ctxt != NULL))
719
1.02M
  node = ctxt->inst;
720
1.11M
    xsltPrintErrorContext(ctxt, style, node);
721
1.11M
    XSLT_GET_VAR_STR(msg, str);
722
1.11M
    error(errctx, "%s", str);
723
1.11M
    if (str != NULL)
724
1.11M
  xmlFree(str);
725
1.11M
}
726
727
/************************************************************************
728
 *                  *
729
 *        QNames          *
730
 *                  *
731
 ************************************************************************/
732
733
/**
734
 * xsltSplitQName:
735
 * @dict: a dictionary
736
 * @name:  the full QName
737
 * @prefix: the return value
738
 *
739
 * Split QNames into prefix and local names, both allocated from a dictionary.
740
 *
741
 * Returns: the localname or NULL in case of error.
742
 */
743
const xmlChar *
744
24.7k
xsltSplitQName(xmlDictPtr dict, const xmlChar *name, const xmlChar **prefix) {
745
24.7k
    int len = 0;
746
24.7k
    const xmlChar *ret = NULL;
747
748
24.7k
    *prefix = NULL;
749
24.7k
    if ((name == NULL) || (dict == NULL)) return(NULL);
750
24.7k
    if (name[0] == ':')
751
25
        return(xmlDictLookup(dict, name, -1));
752
366k
    while ((name[len] != 0) && (name[len] != ':')) len++;
753
24.7k
    if (name[len] == 0) return(xmlDictLookup(dict, name, -1));
754
24.3k
    *prefix = xmlDictLookup(dict, name, len);
755
24.3k
    ret = xmlDictLookup(dict, &name[len + 1], -1);
756
24.3k
    return(ret);
757
24.7k
}
758
759
/**
760
 * xsltGetQNameURI:
761
 * @node:  the node holding the QName
762
 * @name:  pointer to the initial QName value
763
 *
764
 * This function analyzes @name, if the name contains a prefix,
765
 * the function seaches the associated namespace in scope for it.
766
 * It will also replace @name value with the NCName, the old value being
767
 * freed.
768
 * Errors in the prefix lookup are signalled by setting @name to NULL.
769
 *
770
 * NOTE: the namespace returned is a pointer to the place where it is
771
 *       defined and hence has the same lifespan as the document holding it.
772
 *
773
 * Returns the namespace URI if there is a prefix, or NULL if @name is
774
 *         not prefixed.
775
 */
776
const xmlChar *
777
xsltGetQNameURI(xmlNodePtr node, xmlChar ** name)
778
0
{
779
0
    int len = 0;
780
0
    xmlChar *qname;
781
0
    xmlNsPtr ns;
782
783
0
    if (name == NULL)
784
0
  return(NULL);
785
0
    qname = *name;
786
0
    if ((qname == NULL) || (*qname == 0))
787
0
  return(NULL);
788
0
    if (node == NULL) {
789
0
  xsltGenericError(xsltGenericErrorContext,
790
0
             "QName: no element for namespace lookup %s\n",
791
0
       qname);
792
0
  xmlFree(qname);
793
0
  *name = NULL;
794
0
  return(NULL);
795
0
    }
796
797
    /* nasty but valid */
798
0
    if (qname[0] == ':')
799
0
  return(NULL);
800
801
    /*
802
     * we are not trying to validate but just to cut, and yes it will
803
     * work even if this is a set of UTF-8 encoded chars
804
     */
805
0
    while ((qname[len] != 0) && (qname[len] != ':'))
806
0
  len++;
807
808
0
    if (qname[len] == 0)
809
0
  return(NULL);
810
811
    /*
812
     * handle xml: separately, this one is magical
813
     */
814
0
    if ((qname[0] == 'x') && (qname[1] == 'm') &&
815
0
        (qname[2] == 'l') && (qname[3] == ':')) {
816
0
  if (qname[4] == 0)
817
0
      return(NULL);
818
0
        *name = xmlStrdup(&qname[4]);
819
0
  xmlFree(qname);
820
0
  return(XML_XML_NAMESPACE);
821
0
    }
822
823
0
    qname[len] = 0;
824
0
    ns = xmlSearchNs(node->doc, node, qname);
825
0
    if (ns == NULL) {
826
0
  xsltGenericError(xsltGenericErrorContext,
827
0
    "%s:%s : no namespace bound to prefix %s\n",
828
0
             qname, &qname[len + 1], qname);
829
0
  *name = NULL;
830
0
  xmlFree(qname);
831
0
  return(NULL);
832
0
    }
833
0
    *name = xmlStrdup(&qname[len + 1]);
834
0
    xmlFree(qname);
835
0
    return(ns->href);
836
0
}
837
838
/**
839
 * xsltGetQNameURI2:
840
 * @style:  stylesheet pointer
841
 * @node:   the node holding the QName
842
 * @name:   pointer to the initial QName value
843
 *
844
 * This function is similar to xsltGetQNameURI, but is used when
845
 * @name is a dictionary entry.
846
 *
847
 * Returns the namespace URI if there is a prefix, or NULL if @name is
848
 * not prefixed.
849
 */
850
const xmlChar *
851
xsltGetQNameURI2(xsltStylesheetPtr style, xmlNodePtr node,
852
0
     const xmlChar **name) {
853
0
    int len = 0;
854
0
    xmlChar *qname;
855
0
    xmlNsPtr ns;
856
857
0
    if (name == NULL)
858
0
        return(NULL);
859
0
    qname = (xmlChar *)*name;
860
0
    if ((qname == NULL) || (*qname == 0))
861
0
        return(NULL);
862
0
    if (node == NULL) {
863
0
        xsltGenericError(xsltGenericErrorContext,
864
0
                         "QName: no element for namespace lookup %s\n",
865
0
                          qname);
866
0
  *name = NULL;
867
0
  return(NULL);
868
0
    }
869
870
    /*
871
     * we are not trying to validate but just to cut, and yes it will
872
     * work even if this is a set of UTF-8 encoded chars
873
     */
874
0
    while ((qname[len] != 0) && (qname[len] != ':'))
875
0
        len++;
876
877
0
    if (qname[len] == 0)
878
0
        return(NULL);
879
880
    /*
881
     * handle xml: separately, this one is magical
882
     */
883
0
    if ((qname[0] == 'x') && (qname[1] == 'm') &&
884
0
        (qname[2] == 'l') && (qname[3] == ':')) {
885
0
        if (qname[4] == 0)
886
0
            return(NULL);
887
0
        *name = xmlDictLookup(style->dict, &qname[4], -1);
888
0
        return(XML_XML_NAMESPACE);
889
0
    }
890
891
0
    qname = xmlStrndup(*name, len);
892
0
    ns = xmlSearchNs(node->doc, node, qname);
893
0
    if (ns == NULL) {
894
0
  if (style) {
895
0
      xsltTransformError(NULL, style, node,
896
0
    "No namespace bound to prefix '%s'.\n",
897
0
    qname);
898
0
      style->errors++;
899
0
  } else {
900
0
      xsltGenericError(xsltGenericErrorContext,
901
0
                "%s : no namespace bound to prefix %s\n",
902
0
    *name, qname);
903
0
  }
904
0
        *name = NULL;
905
0
        xmlFree(qname);
906
0
        return(NULL);
907
0
    }
908
0
    *name = xmlDictLookup(style->dict, (*name)+len+1, -1);
909
0
    xmlFree(qname);
910
0
    return(ns->href);
911
0
}
912
913
/************************************************************************
914
 *                  *
915
 *        Sorting         *
916
 *                  *
917
 ************************************************************************/
918
919
/**
920
 * xsltDocumentSortFunction:
921
 * @list:  the node set
922
 *
923
 * reorder the current node list @list accordingly to the document order
924
 * This function is slow, obsolete and should not be used anymore.
925
 */
926
void
927
0
xsltDocumentSortFunction(xmlNodeSetPtr list) {
928
0
    int i, j;
929
0
    int len, tst;
930
0
    xmlNodePtr node;
931
932
0
    if (list == NULL)
933
0
  return;
934
0
    len = list->nodeNr;
935
0
    if (len <= 1)
936
0
  return;
937
    /* TODO: sort is really not optimized, does it needs to ? */
938
0
    for (i = 0;i < len -1;i++) {
939
0
  for (j = i + 1; j < len; j++) {
940
0
      tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
941
0
      if (tst == -1) {
942
0
    node = list->nodeTab[i];
943
0
    list->nodeTab[i] = list->nodeTab[j];
944
0
    list->nodeTab[j] = node;
945
0
      }
946
0
  }
947
0
    }
948
0
}
949
950
/**
951
 * xsltComputeSortResultInternal:
952
 * @ctxt:  a XSLT process context
953
 * @sort:  xsl:sort node
954
 * @number:  data-type is number
955
 * @locale:  transform strings according to locale
956
 *
957
 * reorder the current node list accordingly to the set of sorting
958
 * requirement provided by the array of nodes.
959
 *
960
 * Returns a ordered XPath nodeset or NULL in case of error.
961
 */
962
static xmlXPathObjectPtr *
963
xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
964
0
                              int number, void *locale) {
965
#ifdef XSLT_REFACTORED
966
    xsltStyleItemSortPtr comp;
967
#else
968
0
    const xsltStylePreComp *comp;
969
0
#endif
970
0
    xmlXPathObjectPtr *results = NULL;
971
0
    xmlNodeSetPtr list = NULL;
972
0
    xmlXPathObjectPtr res;
973
0
    int len = 0;
974
0
    int i;
975
0
    xmlNodePtr oldNode;
976
0
    xmlNodePtr oldInst;
977
0
    int oldPos, oldSize ;
978
0
    int oldNsNr;
979
0
    xmlNsPtr *oldNamespaces;
980
981
0
    comp = sort->psvi;
982
0
    if (comp == NULL) {
983
0
  xsltGenericError(xsltGenericErrorContext,
984
0
       "xsl:sort : compilation failed\n");
985
0
  return(NULL);
986
0
    }
987
988
0
    if ((comp->select == NULL) || (comp->comp == NULL))
989
0
  return(NULL);
990
991
0
    list = ctxt->nodeList;
992
0
    if ((list == NULL) || (list->nodeNr <= 1))
993
0
  return(NULL);
994
995
0
    len = list->nodeNr;
996
997
    /* TODO: xsl:sort lang attribute */
998
    /* TODO: xsl:sort case-order attribute */
999
1000
1001
0
    results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
1002
0
    if (results == NULL) {
1003
0
  xsltGenericError(xsltGenericErrorContext,
1004
0
       "xsltComputeSortResult: memory allocation failure\n");
1005
0
  return(NULL);
1006
0
    }
1007
1008
0
    oldNode = ctxt->node;
1009
0
    oldInst = ctxt->inst;
1010
0
    oldPos = ctxt->xpathCtxt->proximityPosition;
1011
0
    oldSize = ctxt->xpathCtxt->contextSize;
1012
0
    oldNsNr = ctxt->xpathCtxt->nsNr;
1013
0
    oldNamespaces = ctxt->xpathCtxt->namespaces;
1014
0
    for (i = 0;i < len;i++) {
1015
0
  ctxt->inst = sort;
1016
0
  ctxt->xpathCtxt->contextSize = len;
1017
0
  ctxt->xpathCtxt->proximityPosition = i + 1;
1018
0
  ctxt->node = list->nodeTab[i];
1019
0
  ctxt->xpathCtxt->node = ctxt->node;
1020
#ifdef XSLT_REFACTORED
1021
  if (comp->inScopeNs != NULL) {
1022
      ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
1023
      ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
1024
  } else {
1025
      ctxt->xpathCtxt->namespaces = NULL;
1026
      ctxt->xpathCtxt->nsNr = 0;
1027
  }
1028
#else
1029
0
  ctxt->xpathCtxt->namespaces = comp->nsList;
1030
0
  ctxt->xpathCtxt->nsNr = comp->nsNr;
1031
0
#endif
1032
0
  res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1033
0
  if (res != NULL) {
1034
0
      if (res->type != XPATH_STRING)
1035
0
    res = xmlXPathConvertString(res);
1036
0
      if (number)
1037
0
    res = xmlXPathConvertNumber(res);
1038
0
      res->index = i; /* Save original pos for dupl resolv */
1039
0
      if (number) {
1040
0
    if (res->type == XPATH_NUMBER) {
1041
0
        results[i] = res;
1042
0
    } else {
1043
#ifdef WITH_XSLT_DEBUG_PROCESS
1044
        xsltGenericDebug(xsltGenericDebugContext,
1045
      "xsltComputeSortResult: select didn't evaluate to a number\n");
1046
#endif
1047
0
        results[i] = NULL;
1048
0
    }
1049
0
      } else {
1050
0
    if (res->type == XPATH_STRING) {
1051
0
        if (locale != NULL) {
1052
0
      xmlChar *str = res->stringval;
1053
0
                        xmlChar *sortKey = ctxt->genSortKey(locale, str);
1054
1055
0
                        if (sortKey == NULL) {
1056
0
                            xsltTransformError(ctxt, NULL, sort,
1057
0
                                "xsltComputeSortResult: sort key is null\n");
1058
0
                        } else {
1059
0
                            res->stringval = sortKey;
1060
0
                            xmlFree(str);
1061
0
                        }
1062
0
        }
1063
1064
0
        results[i] = res;
1065
0
    } else {
1066
#ifdef WITH_XSLT_DEBUG_PROCESS
1067
        xsltGenericDebug(xsltGenericDebugContext,
1068
      "xsltComputeSortResult: select didn't evaluate to a string\n");
1069
#endif
1070
0
        results[i] = NULL;
1071
0
    }
1072
0
      }
1073
0
  } else {
1074
0
      ctxt->state = XSLT_STATE_STOPPED;
1075
0
      results[i] = NULL;
1076
0
  }
1077
0
    }
1078
0
    ctxt->node = oldNode;
1079
0
    ctxt->inst = oldInst;
1080
0
    ctxt->xpathCtxt->contextSize = oldSize;
1081
0
    ctxt->xpathCtxt->proximityPosition = oldPos;
1082
0
    ctxt->xpathCtxt->nsNr = oldNsNr;
1083
0
    ctxt->xpathCtxt->namespaces = oldNamespaces;
1084
1085
0
    return(results);
1086
0
}
1087
1088
/**
1089
 * xsltComputeSortResult:
1090
 * @ctxt:  a XSLT process context
1091
 * @sort:  node list
1092
 *
1093
 * reorder the current node list accordingly to the set of sorting
1094
 * requirement provided by the array of nodes.
1095
 *
1096
 * Returns a ordered XPath nodeset or NULL in case of error.
1097
 */
1098
xmlXPathObjectPtr *
1099
0
xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
1100
0
    const xsltStylePreComp *comp = sort->psvi;
1101
0
    int number = 0;
1102
1103
0
    if (comp != NULL)
1104
0
        number = comp->number;
1105
0
    return xsltComputeSortResultInternal(ctxt, sort, number,
1106
                                         /* locale */ NULL);
1107
0
}
1108
1109
/**
1110
 * xsltDefaultSortFunction:
1111
 * @ctxt:  a XSLT process context
1112
 * @sorts:  array of sort nodes
1113
 * @nbsorts:  the number of sorts in the array
1114
 *
1115
 * reorder the current node list accordingly to the set of sorting
1116
 * requirement provided by the arry of nodes.
1117
 */
1118
void
1119
xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
1120
0
             int nbsorts) {
1121
#ifdef XSLT_REFACTORED
1122
    xsltStyleItemSortPtr comp;
1123
#else
1124
0
    const xsltStylePreComp *comp;
1125
0
#endif
1126
0
    xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
1127
0
    xmlXPathObjectPtr *results = NULL, *res;
1128
0
    xmlNodeSetPtr list = NULL;
1129
0
    int len = 0;
1130
0
    int i, j, incr;
1131
0
    int tst;
1132
0
    int depth;
1133
0
    xmlNodePtr node;
1134
0
    xmlXPathObjectPtr tmp;
1135
0
    int number[XSLT_MAX_SORT], desc[XSLT_MAX_SORT];
1136
0
    void *locale[XSLT_MAX_SORT];
1137
1138
0
    if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
1139
0
  (nbsorts >= XSLT_MAX_SORT))
1140
0
  return;
1141
0
    if (sorts[0] == NULL)
1142
0
  return;
1143
0
    comp = sorts[0]->psvi;
1144
0
    if (comp == NULL)
1145
0
  return;
1146
1147
0
    list = ctxt->nodeList;
1148
0
    if ((list == NULL) || (list->nodeNr <= 1))
1149
0
  return; /* nothing to do */
1150
1151
0
    for (j = 0; j < nbsorts; j++) {
1152
0
        xmlChar *lang;
1153
1154
0
  comp = sorts[j]->psvi;
1155
0
  if ((comp->stype == NULL) && (comp->has_stype != 0)) {
1156
0
      xmlChar *stype =
1157
0
    xsltEvalAttrValueTemplate(ctxt, sorts[j],
1158
0
            BAD_CAST "data-type", NULL);
1159
0
      number[j] = 0;
1160
0
      if (stype != NULL) {
1161
0
    if (xmlStrEqual(stype, (const xmlChar *) "text"))
1162
0
        ;
1163
0
    else if (xmlStrEqual(stype, (const xmlChar *) "number"))
1164
0
        number[j] = 1;
1165
0
    else {
1166
0
        xsltTransformError(ctxt, NULL, sorts[j],
1167
0
        "xsltDoSortFunction: no support for data-type = %s\n",
1168
0
        stype);
1169
0
    }
1170
0
                xmlFree(stype);
1171
0
      }
1172
0
  } else {
1173
0
      number[j] = comp->number;
1174
0
        }
1175
0
  if ((comp->order == NULL) && (comp->has_order != 0)) {
1176
0
      xmlChar *order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1177
0
                                                       BAD_CAST "order", NULL);
1178
0
      desc[j] = 0;
1179
0
      if (order != NULL) {
1180
0
    if (xmlStrEqual(order, (const xmlChar *) "ascending"))
1181
0
        ;
1182
0
    else if (xmlStrEqual(order, (const xmlChar *) "descending"))
1183
0
        desc[j] = 1;
1184
0
    else {
1185
0
        xsltTransformError(ctxt, NULL, sorts[j],
1186
0
           "xsltDoSortFunction: invalid value %s for order\n",
1187
0
           order);
1188
0
    }
1189
0
                xmlFree(order);
1190
0
      }
1191
0
  } else {
1192
0
      desc[j] = comp->descending;
1193
0
  }
1194
0
  if ((comp->lang == NULL) && (comp->has_lang != 0)) {
1195
0
            lang = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1196
0
                  (xmlChar *) "lang",
1197
0
                  NULL);
1198
0
  } else {
1199
0
            lang = (xmlChar *) comp->lang;
1200
0
        }
1201
0
        if (lang != NULL) {
1202
0
            locale[j] = ctxt->newLocale(lang, comp->lower_first);
1203
0
            if (lang != comp->lang)
1204
0
                xmlFree(lang);
1205
0
        } else {
1206
0
            locale[j] = NULL;
1207
0
        }
1208
0
    }
1209
1210
0
    len = list->nodeNr;
1211
1212
0
    resultsTab[0] = xsltComputeSortResultInternal(ctxt, sorts[0], number[0],
1213
0
                                                  locale[0]);
1214
0
    for (i = 1;i < XSLT_MAX_SORT;i++)
1215
0
  resultsTab[i] = NULL;
1216
1217
0
    results = resultsTab[0];
1218
1219
0
    comp = sorts[0]->psvi;
1220
0
    if (results == NULL)
1221
0
  goto cleanup;
1222
1223
    /* Shell's sort of node-set */
1224
0
    for (incr = len / 2; incr > 0; incr /= 2) {
1225
0
  for (i = incr; i < len; i++) {
1226
0
      j = i - incr;
1227
0
      if (results[i] == NULL)
1228
0
    continue;
1229
1230
0
      while (j >= 0) {
1231
0
    if (results[j] == NULL)
1232
0
        tst = 1;
1233
0
    else {
1234
0
        if (number[0]) {
1235
      /* We make NaN smaller than number in accordance
1236
         with XSLT spec */
1237
0
      if (xmlXPathIsNaN(results[j]->floatval)) {
1238
0
          if (xmlXPathIsNaN(results[j + incr]->floatval))
1239
0
        tst = 0;
1240
0
          else
1241
0
        tst = -1;
1242
0
      } else if (xmlXPathIsNaN(results[j + incr]->floatval))
1243
0
          tst = 1;
1244
0
      else if (results[j]->floatval ==
1245
0
        results[j + incr]->floatval)
1246
0
          tst = 0;
1247
0
      else if (results[j]->floatval >
1248
0
        results[j + incr]->floatval)
1249
0
          tst = 1;
1250
0
      else tst = -1;
1251
0
        } else {
1252
0
      tst = xmlStrcmp(results[j]->stringval,
1253
0
             results[j + incr]->stringval);
1254
0
        }
1255
0
        if (desc[0])
1256
0
      tst = -tst;
1257
0
    }
1258
0
    if (tst == 0) {
1259
        /*
1260
         * Okay we need to use multi level sorts
1261
         */
1262
0
        depth = 1;
1263
0
        while (depth < nbsorts) {
1264
0
      if (sorts[depth] == NULL)
1265
0
          break;
1266
0
      comp = sorts[depth]->psvi;
1267
0
      if (comp == NULL)
1268
0
          break;
1269
1270
      /*
1271
       * Compute the result of the next level for the
1272
       * full set, this might be optimized ... or not
1273
       */
1274
0
      if (resultsTab[depth] == NULL)
1275
0
          resultsTab[depth] =
1276
0
                                xsltComputeSortResultInternal(ctxt,
1277
0
                                                              sorts[depth],
1278
0
                                                              number[depth],
1279
0
                                                              locale[depth]);
1280
0
      res = resultsTab[depth];
1281
0
      if (res == NULL)
1282
0
          break;
1283
0
      if (res[j] == NULL) {
1284
0
          if (res[j+incr] != NULL)
1285
0
        tst = 1;
1286
0
      } else if (res[j+incr] == NULL) {
1287
0
          tst = -1;
1288
0
      } else {
1289
0
          if (number[depth]) {
1290
        /* We make NaN smaller than number in
1291
           accordance with XSLT spec */
1292
0
        if (xmlXPathIsNaN(res[j]->floatval)) {
1293
0
            if (xmlXPathIsNaN(res[j +
1294
0
            incr]->floatval))
1295
0
          tst = 0;
1296
0
            else
1297
0
                tst = -1;
1298
0
        } else if (xmlXPathIsNaN(res[j + incr]->
1299
0
            floatval))
1300
0
            tst = 1;
1301
0
        else if (res[j]->floatval == res[j + incr]->
1302
0
            floatval)
1303
0
            tst = 0;
1304
0
        else if (res[j]->floatval >
1305
0
          res[j + incr]->floatval)
1306
0
            tst = 1;
1307
0
        else tst = -1;
1308
0
          } else {
1309
0
        tst = xmlStrcmp(res[j]->stringval,
1310
0
               res[j + incr]->stringval);
1311
0
          }
1312
0
          if (desc[depth])
1313
0
        tst = -tst;
1314
0
      }
1315
1316
      /*
1317
       * if we still can't differenciate at this level
1318
       * try one level deeper.
1319
       */
1320
0
      if (tst != 0)
1321
0
          break;
1322
0
      depth++;
1323
0
        }
1324
0
    }
1325
0
    if (tst == 0) {
1326
0
        tst = results[j]->index > results[j + incr]->index;
1327
0
    }
1328
0
    if (tst > 0) {
1329
0
        tmp = results[j];
1330
0
        results[j] = results[j + incr];
1331
0
        results[j + incr] = tmp;
1332
0
        node = list->nodeTab[j];
1333
0
        list->nodeTab[j] = list->nodeTab[j + incr];
1334
0
        list->nodeTab[j + incr] = node;
1335
0
        depth = 1;
1336
0
        while (depth < nbsorts) {
1337
0
      if (sorts[depth] == NULL)
1338
0
          break;
1339
0
      if (resultsTab[depth] == NULL)
1340
0
          break;
1341
0
      res = resultsTab[depth];
1342
0
      tmp = res[j];
1343
0
      res[j] = res[j + incr];
1344
0
      res[j + incr] = tmp;
1345
0
      depth++;
1346
0
        }
1347
0
        j -= incr;
1348
0
    } else
1349
0
        break;
1350
0
      }
1351
0
  }
1352
0
    }
1353
1354
0
cleanup:
1355
0
    for (j = 0; j < nbsorts; j++) {
1356
0
        if (locale[j] != NULL) {
1357
0
            ctxt->freeLocale(locale[j]);
1358
0
        }
1359
0
  if (resultsTab[j] != NULL) {
1360
0
      for (i = 0;i < len;i++)
1361
0
    xmlXPathFreeObject(resultsTab[j][i]);
1362
0
      xmlFree(resultsTab[j]);
1363
0
  }
1364
0
    }
1365
0
}
1366
1367
1368
static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1369
1370
/**
1371
 * xsltDoSortFunction:
1372
 * @ctxt:  a XSLT process context
1373
 * @sorts:  array of sort nodes
1374
 * @nbsorts:  the number of sorts in the array
1375
 *
1376
 * reorder the current node list accordingly to the set of sorting
1377
 * requirement provided by the arry of nodes.
1378
 * This is a wrapper function, the actual function used is specified
1379
 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1380
 * or xsltSetSortFunc() to set the global sort function.
1381
 * If a sort function is set on the context, this will get called.
1382
 * Otherwise the global sort function is called.
1383
 */
1384
void
1385
xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1386
                   int nbsorts)
1387
0
{
1388
0
    if (ctxt->sortfunc != NULL)
1389
0
  (ctxt->sortfunc)(ctxt, sorts, nbsorts);
1390
0
    else if (xsltSortFunction != NULL)
1391
0
        xsltSortFunction(ctxt, sorts, nbsorts);
1392
0
}
1393
1394
/**
1395
 * xsltSetSortFunc:
1396
 * @handler:  the new handler function
1397
 *
1398
 * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1399
 *
1400
 * Function to reset the global handler for XSLT sorting.
1401
 * If the handler is NULL, the default sort function will be used.
1402
 */
1403
void
1404
0
xsltSetSortFunc(xsltSortFunc handler) {
1405
0
    if (handler != NULL)
1406
0
  xsltSortFunction = handler;
1407
0
    else
1408
0
  xsltSortFunction = xsltDefaultSortFunction;
1409
0
}
1410
1411
/**
1412
 * xsltSetCtxtSortFunc:
1413
 * @ctxt:  a XSLT process context
1414
 * @handler:  the new handler function
1415
 *
1416
 * DEPRECATED: Use xsltSetCtxtLocaleHandlers.
1417
 *
1418
 * Function to set the handler for XSLT sorting
1419
 * for the specified context.
1420
 * If the handler is NULL, then the global
1421
 * sort function will be called
1422
 */
1423
void
1424
0
xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1425
0
    ctxt->sortfunc = handler;
1426
0
}
1427
1428
/**
1429
 * xsltSetCtxtLocaleHandlers:
1430
 * @ctxt:  an XSLT transform context
1431
 * @newLocale:  locale constructor
1432
 * @freeLocale:  locale destructor
1433
 * @genSortKey  sort key generator
1434
 *
1435
 * Set the locale handlers.
1436
 */
1437
void
1438
xsltSetCtxtLocaleHandlers(xsltTransformContextPtr ctxt,
1439
                          xsltNewLocaleFunc newLocale,
1440
                          xsltFreeLocaleFunc freeLocale,
1441
0
                          xsltGenSortKeyFunc genSortKey) {
1442
0
    if (ctxt == NULL)
1443
0
        return;
1444
1445
0
    ctxt->newLocale = newLocale;
1446
0
    ctxt->freeLocale = freeLocale;
1447
0
    ctxt->genSortKey = genSortKey;
1448
0
}
1449
1450
/************************************************************************
1451
 *                  *
1452
 *        Parsing options       *
1453
 *                  *
1454
 ************************************************************************/
1455
1456
/**
1457
 * xsltSetCtxtParseOptions:
1458
 * @ctxt:  a XSLT process context
1459
 * @options:  a combination of libxml2 xmlParserOption
1460
 *
1461
 * Change the default parser option passed by the XSLT engine to the
1462
 * parser when using document() loading.
1463
 *
1464
 * Returns the previous options or -1 in case of error
1465
 */
1466
int
1467
xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1468
0
{
1469
0
    int oldopts;
1470
1471
0
    if (ctxt == NULL)
1472
0
        return(-1);
1473
0
    oldopts = ctxt->parserOptions;
1474
0
    if (ctxt->xinclude)
1475
0
        oldopts |= XML_PARSE_XINCLUDE;
1476
0
    ctxt->parserOptions = options;
1477
0
    if (options & XML_PARSE_XINCLUDE)
1478
0
        ctxt->xinclude = 1;
1479
0
    else
1480
0
        ctxt->xinclude = 0;
1481
0
    return(oldopts);
1482
0
}
1483
1484
/************************************************************************
1485
 *                  *
1486
 *        Output          *
1487
 *                  *
1488
 ************************************************************************/
1489
1490
/**
1491
 * xsltSaveResultTo:
1492
 * @buf:  an output buffer
1493
 * @result:  the result xmlDocPtr
1494
 * @style:  the stylesheet
1495
 *
1496
 * Save the result @result obtained by applying the @style stylesheet
1497
 * to an I/O output channel @buf
1498
 *
1499
 * Returns the number of byte written or -1 in case of failure.
1500
 */
1501
int
1502
xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1503
0
         xsltStylesheetPtr style) {
1504
0
    const xmlChar *encoding;
1505
0
    int base;
1506
0
    const xmlChar *method;
1507
0
    int indent;
1508
1509
0
    if ((buf == NULL) || (result == NULL) || (style == NULL))
1510
0
  return(-1);
1511
0
    if ((result->children == NULL) ||
1512
0
  ((result->children->type == XML_DTD_NODE) &&
1513
0
   (result->children->next == NULL)))
1514
0
  return(0);
1515
1516
0
    if ((style->methodURI != NULL) &&
1517
0
  ((style->method == NULL) ||
1518
0
   (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1519
0
        xsltGenericError(xsltGenericErrorContext,
1520
0
    "xsltSaveResultTo : unknown output method\n");
1521
0
        return(-1);
1522
0
    }
1523
1524
0
    base = buf->written;
1525
1526
0
    XSLT_GET_IMPORT_PTR(method, style, method)
1527
0
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1528
0
    XSLT_GET_IMPORT_INT(indent, style, indent);
1529
1530
0
    if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1531
0
  method = (const xmlChar *) "html";
1532
1533
0
    if ((method != NULL) &&
1534
0
  (xmlStrEqual(method, (const xmlChar *) "html"))) {
1535
0
  if (encoding != NULL) {
1536
0
      htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1537
0
  } else {
1538
0
      htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1539
0
  }
1540
0
  if (indent == -1)
1541
0
      indent = 1;
1542
0
  htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1543
0
                           indent);
1544
0
  xmlOutputBufferFlush(buf);
1545
0
    } else if ((method != NULL) &&
1546
0
  (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1547
0
  if (encoding != NULL) {
1548
0
      htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1549
0
  } else {
1550
0
      htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1551
0
  }
1552
0
  htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1553
0
  xmlOutputBufferFlush(buf);
1554
0
    } else if ((method != NULL) &&
1555
0
         (xmlStrEqual(method, (const xmlChar *) "text"))) {
1556
0
  xmlNodePtr cur;
1557
1558
0
  cur = result->children;
1559
0
  while (cur != NULL) {
1560
0
      if (cur->type == XML_TEXT_NODE)
1561
0
    xmlOutputBufferWriteString(buf, (const char *) cur->content);
1562
1563
      /*
1564
       * Skip to next node
1565
       */
1566
0
      if (cur->children != NULL) {
1567
0
    if ((cur->children->type != XML_ENTITY_DECL) &&
1568
0
        (cur->children->type != XML_ENTITY_REF_NODE) &&
1569
0
        (cur->children->type != XML_ENTITY_NODE)) {
1570
0
        cur = cur->children;
1571
0
        continue;
1572
0
    }
1573
0
      }
1574
0
      if (cur->next != NULL) {
1575
0
    cur = cur->next;
1576
0
    continue;
1577
0
      }
1578
1579
0
      do {
1580
0
    cur = cur->parent;
1581
0
    if (cur == NULL)
1582
0
        break;
1583
0
    if (cur == (xmlNodePtr) style->doc) {
1584
0
        cur = NULL;
1585
0
        break;
1586
0
    }
1587
0
    if (cur->next != NULL) {
1588
0
        cur = cur->next;
1589
0
        break;
1590
0
    }
1591
0
      } while (cur != NULL);
1592
0
  }
1593
0
  xmlOutputBufferFlush(buf);
1594
0
    } else {
1595
0
  int omitXmlDecl;
1596
0
  int standalone;
1597
1598
0
  XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1599
0
  XSLT_GET_IMPORT_INT(standalone, style, standalone);
1600
1601
0
  if (omitXmlDecl != 1) {
1602
0
      xmlOutputBufferWriteString(buf, "<?xml version=");
1603
0
      if (result->version != NULL) {
1604
0
    xmlOutputBufferWriteString(buf, "\"");
1605
0
    xmlOutputBufferWriteString(buf, (const char *)result->version);
1606
0
    xmlOutputBufferWriteString(buf, "\"");
1607
0
      } else
1608
0
    xmlOutputBufferWriteString(buf, "\"1.0\"");
1609
0
      if (encoding == NULL) {
1610
0
    if (result->encoding != NULL)
1611
0
        encoding = result->encoding;
1612
0
    else if (result->charset != XML_CHAR_ENCODING_UTF8)
1613
0
        encoding = (const xmlChar *)
1614
0
             xmlGetCharEncodingName((xmlCharEncoding)
1615
0
                                    result->charset);
1616
0
      }
1617
0
      if (encoding != NULL) {
1618
0
    xmlOutputBufferWriteString(buf, " encoding=");
1619
0
    xmlOutputBufferWriteString(buf, "\"");
1620
0
    xmlOutputBufferWriteString(buf, (const char *) encoding);
1621
0
    xmlOutputBufferWriteString(buf, "\"");
1622
0
      }
1623
0
      switch (standalone) {
1624
0
    case 0:
1625
0
        xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1626
0
        break;
1627
0
    case 1:
1628
0
        xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1629
0
        break;
1630
0
    default:
1631
0
        break;
1632
0
      }
1633
0
      xmlOutputBufferWriteString(buf, "?>\n");
1634
0
  }
1635
0
  if (result->children != NULL) {
1636
0
            xmlNodePtr children = result->children;
1637
0
      xmlNodePtr child = children;
1638
1639
            /*
1640
             * Hack to avoid quadratic behavior when scanning
1641
             * result->children in xmlGetIntSubset called by
1642
             * xmlNodeDumpOutput.
1643
             */
1644
0
            result->children = NULL;
1645
1646
0
      while (child != NULL) {
1647
0
    xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1648
0
                (const char *) encoding);
1649
0
    if (indent && ((child->type == XML_DTD_NODE) ||
1650
0
        ((child->type == XML_COMMENT_NODE) &&
1651
0
         (child->next != NULL))))
1652
0
        xmlOutputBufferWriteString(buf, "\n");
1653
0
    child = child->next;
1654
0
      }
1655
0
      if (indent)
1656
0
      xmlOutputBufferWriteString(buf, "\n");
1657
1658
0
            result->children = children;
1659
0
  }
1660
0
  xmlOutputBufferFlush(buf);
1661
0
    }
1662
0
    return(buf->written - base);
1663
0
}
1664
1665
/**
1666
 * xsltSaveResultToFilename:
1667
 * @URL:  a filename or URL
1668
 * @result:  the result xmlDocPtr
1669
 * @style:  the stylesheet
1670
 * @compression:  the compression factor (0 - 9 included)
1671
 *
1672
 * Save the result @result obtained by applying the @style stylesheet
1673
 * to a file or @URL
1674
 *
1675
 * Returns the number of byte written or -1 in case of failure.
1676
 */
1677
int
1678
xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1679
0
       xsltStylesheetPtr style, int compression) {
1680
0
    xmlOutputBufferPtr buf;
1681
0
    const xmlChar *encoding;
1682
0
    int ret;
1683
1684
0
    if ((URL == NULL) || (result == NULL) || (style == NULL))
1685
0
  return(-1);
1686
0
    if (result->children == NULL)
1687
0
  return(0);
1688
1689
0
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1690
0
    if (encoding != NULL) {
1691
0
  xmlCharEncodingHandlerPtr encoder;
1692
1693
0
  encoder = xmlFindCharEncodingHandler((char *)encoding);
1694
0
  if ((encoder != NULL) &&
1695
0
      (xmlStrEqual((const xmlChar *)encoder->name,
1696
0
       (const xmlChar *) "UTF-8")))
1697
0
      encoder = NULL;
1698
0
  buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
1699
0
    } else {
1700
0
  buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1701
0
    }
1702
0
    if (buf == NULL)
1703
0
  return(-1);
1704
0
    xsltSaveResultTo(buf, result, style);
1705
0
    ret = xmlOutputBufferClose(buf);
1706
0
    return(ret);
1707
0
}
1708
1709
/**
1710
 * xsltSaveResultToFile:
1711
 * @file:  a FILE * I/O
1712
 * @result:  the result xmlDocPtr
1713
 * @style:  the stylesheet
1714
 *
1715
 * Save the result @result obtained by applying the @style stylesheet
1716
 * to an open FILE * I/O.
1717
 * This does not close the FILE @file
1718
 *
1719
 * Returns the number of bytes written or -1 in case of failure.
1720
 */
1721
int
1722
0
xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1723
0
    xmlOutputBufferPtr buf;
1724
0
    const xmlChar *encoding;
1725
0
    int ret;
1726
1727
0
    if ((file == NULL) || (result == NULL) || (style == NULL))
1728
0
  return(-1);
1729
0
    if (result->children == NULL)
1730
0
  return(0);
1731
1732
0
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1733
0
    if (encoding != NULL) {
1734
0
  xmlCharEncodingHandlerPtr encoder;
1735
1736
0
  encoder = xmlFindCharEncodingHandler((char *)encoding);
1737
0
  if ((encoder != NULL) &&
1738
0
      (xmlStrEqual((const xmlChar *)encoder->name,
1739
0
       (const xmlChar *) "UTF-8")))
1740
0
      encoder = NULL;
1741
0
  buf = xmlOutputBufferCreateFile(file, encoder);
1742
0
    } else {
1743
0
  buf = xmlOutputBufferCreateFile(file, NULL);
1744
0
    }
1745
1746
0
    if (buf == NULL)
1747
0
  return(-1);
1748
0
    xsltSaveResultTo(buf, result, style);
1749
0
    ret = xmlOutputBufferClose(buf);
1750
0
    return(ret);
1751
0
}
1752
1753
/**
1754
 * xsltSaveResultToFd:
1755
 * @fd:  a file descriptor
1756
 * @result:  the result xmlDocPtr
1757
 * @style:  the stylesheet
1758
 *
1759
 * Save the result @result obtained by applying the @style stylesheet
1760
 * to an open file descriptor
1761
 * This does not close the descriptor.
1762
 *
1763
 * Returns the number of bytes written or -1 in case of failure.
1764
 */
1765
int
1766
0
xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1767
0
    xmlOutputBufferPtr buf;
1768
0
    const xmlChar *encoding;
1769
0
    int ret;
1770
1771
0
    if ((fd < 0) || (result == NULL) || (style == NULL))
1772
0
  return(-1);
1773
0
    if (result->children == NULL)
1774
0
  return(0);
1775
1776
0
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1777
0
    if (encoding != NULL) {
1778
0
  xmlCharEncodingHandlerPtr encoder;
1779
1780
0
  encoder = xmlFindCharEncodingHandler((char *)encoding);
1781
0
  if ((encoder != NULL) &&
1782
0
      (xmlStrEqual((const xmlChar *)encoder->name,
1783
0
       (const xmlChar *) "UTF-8")))
1784
0
      encoder = NULL;
1785
0
  buf = xmlOutputBufferCreateFd(fd, encoder);
1786
0
    } else {
1787
0
  buf = xmlOutputBufferCreateFd(fd, NULL);
1788
0
    }
1789
0
    if (buf == NULL)
1790
0
  return(-1);
1791
0
    xsltSaveResultTo(buf, result, style);
1792
0
    ret = xmlOutputBufferClose(buf);
1793
0
    return(ret);
1794
0
}
1795
1796
/**
1797
 * xsltSaveResultToString:
1798
 * @doc_txt_ptr:  Memory pointer for allocated XML text
1799
 * @doc_txt_len:  Length of the generated XML text
1800
 * @result:  the result xmlDocPtr
1801
 * @style:  the stylesheet
1802
 *
1803
 * Save the result @result obtained by applying the @style stylesheet
1804
 * to a new allocated string.
1805
 *
1806
 * Returns 0 in case of success and -1 in case of error
1807
 */
1808
int
1809
xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1810
0
           xmlDocPtr result, xsltStylesheetPtr style) {
1811
0
    xmlOutputBufferPtr buf;
1812
0
    const xmlChar *encoding;
1813
1814
0
    *doc_txt_ptr = NULL;
1815
0
    *doc_txt_len = 0;
1816
0
    if (result->children == NULL)
1817
0
  return(0);
1818
1819
0
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1820
0
    if (encoding != NULL) {
1821
0
  xmlCharEncodingHandlerPtr encoder;
1822
1823
0
  encoder = xmlFindCharEncodingHandler((char *)encoding);
1824
0
  if ((encoder != NULL) &&
1825
0
      (xmlStrEqual((const xmlChar *)encoder->name,
1826
0
       (const xmlChar *) "UTF-8")))
1827
0
      encoder = NULL;
1828
0
  buf = xmlAllocOutputBuffer(encoder);
1829
0
    } else {
1830
0
  buf = xmlAllocOutputBuffer(NULL);
1831
0
    }
1832
0
    if (buf == NULL)
1833
0
  return(-1);
1834
0
    xsltSaveResultTo(buf, result, style);
1835
0
#ifdef LIBXML2_NEW_BUFFER
1836
0
    if (buf->conv != NULL) {
1837
0
  *doc_txt_len = xmlBufUse(buf->conv);
1838
0
  *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
1839
0
    } else {
1840
0
  *doc_txt_len = xmlBufUse(buf->buffer);
1841
0
  *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
1842
0
    }
1843
#else
1844
    if (buf->conv != NULL) {
1845
  *doc_txt_len = buf->conv->use;
1846
  *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1847
    } else {
1848
  *doc_txt_len = buf->buffer->use;
1849
  *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1850
    }
1851
#endif
1852
0
    (void)xmlOutputBufferClose(buf);
1853
0
    return 0;
1854
0
}
1855
1856
/**
1857
 * xsltGetSourceNodeFlags:
1858
 * @node:  Node from source document
1859
 *
1860
 * Returns the flags for a source node.
1861
 */
1862
int
1863
180
xsltGetSourceNodeFlags(xmlNodePtr node) {
1864
    /*
1865
     * Squeeze the bit flags into the upper bits of
1866
     *
1867
     * - 'int properties' member in struct _xmlDoc
1868
     * - 'xmlAttributeType atype' member in struct _xmlAttr
1869
     * - 'unsigned short extra' member in struct _xmlNode
1870
     */
1871
180
    switch (node->type) {
1872
93
        case XML_DOCUMENT_NODE:
1873
93
        case XML_HTML_DOCUMENT_NODE:
1874
93
            return ((xmlDocPtr) node)->properties >> 27;
1875
1876
4
        case XML_ATTRIBUTE_NODE:
1877
4
            return ((xmlAttrPtr) node)->atype >> 27;
1878
1879
73
        case XML_ELEMENT_NODE:
1880
80
        case XML_TEXT_NODE:
1881
80
        case XML_CDATA_SECTION_NODE:
1882
82
        case XML_PI_NODE:
1883
83
        case XML_COMMENT_NODE:
1884
83
            return node->extra >> 12;
1885
1886
0
        default:
1887
0
            return 0;
1888
180
    }
1889
180
}
1890
1891
/**
1892
 * xsltSetSourceNodeFlags:
1893
 * @node:  Node from source document
1894
 * @flags:  Flags
1895
 *
1896
 * Sets the specified flags to 1.
1897
 *
1898
 * Returns 0 on success, -1 on error.
1899
 */
1900
int
1901
xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
1902
153
                       int flags) {
1903
153
    if (node->doc == ctxt->initialContextDoc)
1904
0
        ctxt->sourceDocDirty = 1;
1905
1906
153
    switch (node->type) {
1907
79
        case XML_DOCUMENT_NODE:
1908
79
        case XML_HTML_DOCUMENT_NODE:
1909
79
            ((xmlDocPtr) node)->properties |= flags << 27;
1910
79
            return 0;
1911
1912
4
        case XML_ATTRIBUTE_NODE:
1913
4
            ((xmlAttrPtr) node)->atype |= flags << 27;
1914
4
            return 0;
1915
1916
60
        case XML_ELEMENT_NODE:
1917
67
        case XML_TEXT_NODE:
1918
67
        case XML_CDATA_SECTION_NODE:
1919
69
        case XML_PI_NODE:
1920
70
        case XML_COMMENT_NODE:
1921
70
            node->extra |= flags << 12;
1922
70
            return 0;
1923
1924
0
        default:
1925
0
            return -1;
1926
153
    }
1927
153
}
1928
1929
/**
1930
 * xsltClearSourceNodeFlags:
1931
 * @node:  Node from source document
1932
 * @flags:  Flags
1933
 *
1934
 * Sets the specified flags to 0.
1935
 *
1936
 * Returns 0 on success, -1 on error.
1937
 */
1938
int
1939
0
xsltClearSourceNodeFlags(xmlNodePtr node, int flags) {
1940
0
    switch (node->type) {
1941
0
        case XML_DOCUMENT_NODE:
1942
0
        case XML_HTML_DOCUMENT_NODE:
1943
0
            ((xmlDocPtr) node)->properties &= ~(flags << 27);
1944
0
            return 0;
1945
1946
0
        case XML_ATTRIBUTE_NODE:
1947
0
            ((xmlAttrPtr) node)->atype &= ~(flags << 27);
1948
0
            return 0;
1949
1950
0
        case XML_ELEMENT_NODE:
1951
0
        case XML_TEXT_NODE:
1952
0
        case XML_CDATA_SECTION_NODE:
1953
0
        case XML_PI_NODE:
1954
0
        case XML_COMMENT_NODE:
1955
0
            node->extra &= ~(flags << 12);
1956
0
            return 0;
1957
1958
0
        default:
1959
0
            return -1;
1960
0
    }
1961
0
}
1962
1963
/**
1964
 * xsltGetPSVIPtr:
1965
 * @cur:  Node
1966
 *
1967
 * Returns a pointer to the psvi member of a node or NULL on error.
1968
 */
1969
void **
1970
180
xsltGetPSVIPtr(xmlNodePtr cur) {
1971
180
    switch (cur->type) {
1972
93
        case XML_DOCUMENT_NODE:
1973
93
        case XML_HTML_DOCUMENT_NODE:
1974
93
            return &((xmlDocPtr) cur)->psvi;
1975
1976
4
        case XML_ATTRIBUTE_NODE:
1977
4
            return &((xmlAttrPtr) cur)->psvi;
1978
1979
73
        case XML_ELEMENT_NODE:
1980
80
        case XML_TEXT_NODE:
1981
80
        case XML_CDATA_SECTION_NODE:
1982
82
        case XML_PI_NODE:
1983
83
        case XML_COMMENT_NODE:
1984
83
            return &cur->psvi;
1985
1986
0
        default:
1987
0
            return NULL;
1988
180
    }
1989
180
}
1990
1991
#ifdef WITH_PROFILER
1992
1993
/************************************************************************
1994
 *                  *
1995
 *    Generating profiling information      *
1996
 *                  *
1997
 ************************************************************************/
1998
1999
static long calibration = -1;
2000
2001
/**
2002
 * xsltCalibrateTimestamps:
2003
 *
2004
 * Used for to calibrate the xsltTimestamp() function
2005
 * Should work if launched at startup and we don't loose our quantum :-)
2006
 *
2007
 * Returns the number of milliseconds used by xsltTimestamp()
2008
 */
2009
#if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
2010
    (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
2011
static long
2012
xsltCalibrateTimestamps(void) {
2013
    register int i;
2014
2015
    for (i = 0;i < 999;i++)
2016
  xsltTimestamp();
2017
    return(xsltTimestamp() / 1000);
2018
}
2019
#endif
2020
2021
/**
2022
 * xsltCalibrateAdjust:
2023
 * @delta:  a negative dealy value found
2024
 *
2025
 * Used for to correct the calibration for xsltTimestamp()
2026
 */
2027
void
2028
xsltCalibrateAdjust(long delta) {
2029
    calibration += delta;
2030
}
2031
2032
/**
2033
 * xsltTimestamp:
2034
 *
2035
 * Used for gathering profiling data
2036
 *
2037
 * Returns the number of tenth of milliseconds since the beginning of the
2038
 * profiling
2039
 */
2040
long
2041
xsltTimestamp(void)
2042
{
2043
#ifdef XSLT_WIN32_PERFORMANCE_COUNTER
2044
    BOOL ok;
2045
    LARGE_INTEGER performanceCount;
2046
    LARGE_INTEGER performanceFrequency;
2047
    LONGLONG quadCount;
2048
    double seconds;
2049
    static LONGLONG startupQuadCount = 0;
2050
    static LONGLONG startupQuadFreq = 0;
2051
2052
    ok = QueryPerformanceCounter(&performanceCount);
2053
    if (!ok)
2054
        return 0;
2055
    quadCount = performanceCount.QuadPart;
2056
    if (calibration < 0) {
2057
        calibration = 0;
2058
        ok = QueryPerformanceFrequency(&performanceFrequency);
2059
        if (!ok)
2060
            return 0;
2061
        startupQuadFreq = performanceFrequency.QuadPart;
2062
        startupQuadCount = quadCount;
2063
        return (0);
2064
    }
2065
    if (startupQuadFreq == 0)
2066
        return 0;
2067
    seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
2068
    return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
2069
2070
#else /* XSLT_WIN32_PERFORMANCE_COUNTER */
2071
#ifdef HAVE_CLOCK_GETTIME
2072
#  if defined(CLOCK_MONOTONIC)
2073
#    define XSLT_CLOCK CLOCK_MONOTONIC
2074
#  elif defined(CLOCK_HIGHRES)
2075
#    define XSLT_CLOCK CLOCK_HIGHRES
2076
#  else
2077
#    define XSLT_CLOCK CLOCK_REALTIME
2078
#  endif
2079
    static struct timespec startup;
2080
    struct timespec cur;
2081
    long tics;
2082
2083
    if (calibration < 0) {
2084
        clock_gettime(XSLT_CLOCK, &startup);
2085
        calibration = 0;
2086
        calibration = xsltCalibrateTimestamps();
2087
        clock_gettime(XSLT_CLOCK, &startup);
2088
        return (0);
2089
    }
2090
2091
    clock_gettime(XSLT_CLOCK, &cur);
2092
    tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
2093
    tics += (cur.tv_nsec - startup.tv_nsec) /
2094
                          (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
2095
2096
    tics -= calibration;
2097
    return(tics);
2098
2099
#elif HAVE_GETTIMEOFDAY
2100
    static struct timeval startup;
2101
    struct timeval cur;
2102
    long tics;
2103
2104
    if (calibration < 0) {
2105
        gettimeofday(&startup, NULL);
2106
        calibration = 0;
2107
        calibration = xsltCalibrateTimestamps();
2108
        gettimeofday(&startup, NULL);
2109
        return (0);
2110
    }
2111
2112
    gettimeofday(&cur, NULL);
2113
    tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
2114
    tics += (cur.tv_usec - startup.tv_usec) /
2115
                          (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
2116
2117
    tics -= calibration;
2118
    return(tics);
2119
#else
2120
2121
    /* Neither gettimeofday() nor Win32 performance counter available */
2122
2123
    return (0);
2124
2125
#endif /* HAVE_GETTIMEOFDAY */
2126
#endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
2127
}
2128
2129
static char *
2130
pretty_templ_match(xsltTemplatePtr templ) {
2131
  static char dst[1001];
2132
  char *src = (char *)templ->match;
2133
  int i=0,j;
2134
2135
  /* strip white spaces */
2136
  for (j=0; i<1000 && src[j]; i++,j++) {
2137
      for(;src[j]==' ';j++);
2138
      dst[i]=src[j];
2139
  }
2140
  if(i<998 && templ->mode) {
2141
    /* append [mode] */
2142
    dst[i++]='[';
2143
    src=(char *)templ->mode;
2144
    for (j=0; i<999 && src[j]; i++,j++) {
2145
      dst[i]=src[j];
2146
    }
2147
    dst[i++]=']';
2148
  }
2149
  dst[i]='\0';
2150
  return dst;
2151
}
2152
2153
#define MAX_TEMPLATES 10000
2154
2155
/**
2156
 * xsltSaveProfiling:
2157
 * @ctxt:  an XSLT context
2158
 * @output:  a FILE * for saving the information
2159
 *
2160
 * Save the profiling information on @output
2161
 */
2162
void
2163
xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
2164
    int nb, i,j,k,l;
2165
    int max;
2166
    int total;
2167
    unsigned long totalt;
2168
    xsltTemplatePtr *templates;
2169
    xsltStylesheetPtr style;
2170
    xsltTemplatePtr templ1,templ2;
2171
    int *childt;
2172
2173
    if ((output == NULL) || (ctxt == NULL))
2174
  return;
2175
    if (ctxt->profile == 0)
2176
  return;
2177
2178
    nb = 0;
2179
    max = MAX_TEMPLATES;
2180
    templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
2181
    if (templates == NULL)
2182
  return;
2183
2184
    style = ctxt->style;
2185
    while (style != NULL) {
2186
  templ1 = style->templates;
2187
  while (templ1 != NULL) {
2188
      if (nb >= max)
2189
    break;
2190
2191
      if (templ1->nbCalls > 0)
2192
    templates[nb++] = templ1;
2193
      templ1 = templ1->next;
2194
  }
2195
2196
  style = xsltNextImport(style);
2197
    }
2198
2199
    for (i = 0;i < nb -1;i++) {
2200
  for (j = i + 1; j < nb; j++) {
2201
      if ((templates[i]->time <= templates[j]->time) ||
2202
    ((templates[i]->time == templates[j]->time) &&
2203
           (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2204
    templ1 = templates[j];
2205
    templates[j] = templates[i];
2206
    templates[i] = templ1;
2207
      }
2208
  }
2209
    }
2210
2211
2212
    /* print flat profile */
2213
2214
    fprintf(output, "%6s%20s%20s%10s  Calls Tot 100us Avg\n\n",
2215
      "number", "match", "name", "mode");
2216
    total = 0;
2217
    totalt = 0;
2218
    for (i = 0;i < nb;i++) {
2219
         templ1 = templates[i];
2220
  fprintf(output, "%5d ", i);
2221
  if (templ1->match != NULL) {
2222
      if (xmlStrlen(templ1->match) > 20)
2223
    fprintf(output, "%s\n%26s", templ1->match, "");
2224
      else
2225
    fprintf(output, "%20s", templ1->match);
2226
  } else {
2227
      fprintf(output, "%20s", "");
2228
  }
2229
  if (templ1->name != NULL) {
2230
      if (xmlStrlen(templ1->name) > 20)
2231
    fprintf(output, "%s\n%46s", templ1->name, "");
2232
      else
2233
    fprintf(output, "%20s", templ1->name);
2234
  } else {
2235
      fprintf(output, "%20s", "");
2236
  }
2237
  if (templ1->mode != NULL) {
2238
      if (xmlStrlen(templ1->mode) > 10)
2239
    fprintf(output, "%s\n%56s", templ1->mode, "");
2240
      else
2241
    fprintf(output, "%10s", templ1->mode);
2242
  } else {
2243
      fprintf(output, "%10s", "");
2244
  }
2245
  fprintf(output, " %6d", templ1->nbCalls);
2246
  fprintf(output, " %6ld %6ld\n", templ1->time,
2247
    templ1->time / templ1->nbCalls);
2248
  total += templ1->nbCalls;
2249
  totalt += templ1->time;
2250
    }
2251
    fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
2252
2253
2254
    /* print call graph */
2255
2256
    childt = xmlMalloc((nb + 1) * sizeof(int));
2257
    if (childt == NULL)
2258
  return;
2259
2260
    /* precalculate children times */
2261
    for (i = 0; i < nb; i++) {
2262
        templ1 = templates[i];
2263
2264
        childt[i] = 0;
2265
        for (k = 0; k < nb; k++) {
2266
            templ2 = templates[k];
2267
            for (l = 0; l < templ2->templNr; l++) {
2268
                if (templ2->templCalledTab[l] == templ1) {
2269
                    childt[i] +=templ2->time;
2270
                }
2271
            }
2272
        }
2273
    }
2274
    childt[i] = 0;
2275
2276
    fprintf(output, "\nindex %% time    self  children    called     name\n");
2277
2278
    for (i = 0; i < nb; i++) {
2279
        char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
2280
        unsigned long t;
2281
2282
        templ1 = templates[i];
2283
        /* callers */
2284
        for (j = 0; j < templ1->templNr; j++) {
2285
            templ2 = templ1->templCalledTab[j];
2286
            for (k = 0; k < nb; k++) {
2287
              if (templates[k] == templ2)
2288
                break;
2289
            }
2290
            t=templ2?templ2->time:totalt;
2291
            snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
2292
            snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2293
            snprintf(called_str,sizeof(called_str),"%6d/%d",
2294
                templ1->templCountTab[j], /* number of times caller calls 'this' */
2295
                templ1->nbCalls);         /* total number of calls to 'this' */
2296
2297
            fprintf(output, "             %-8s %-8s %-12s     %s [%d]\n",
2298
                times_str,timec_str,called_str,
2299
                (templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
2300
        }
2301
        /* this */
2302
        snprintf(ix_str,sizeof(ix_str),"[%d]",i);
2303
        snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt);
2304
        snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2305
        snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
2306
        fprintf(output, "%-5s %-6s %-8s %-8s %6d     %s [%d]\n",
2307
            ix_str, timep_str,times_str,timec_str,
2308
            templ1->nbCalls,
2309
            templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
2310
        /* callees
2311
         * - go over templates[0..nb] and their templCalledTab[]
2312
         * - print those where we in the the call-stack
2313
         */
2314
        total = 0;
2315
        for (k = 0; k < nb; k++) {
2316
            templ2 = templates[k];
2317
            for (l = 0; l < templ2->templNr; l++) {
2318
                if (templ2->templCalledTab[l] == templ1) {
2319
                    total+=templ2->templCountTab[l];
2320
                }
2321
            }
2322
        }
2323
        for (k = 0; k < nb; k++) {
2324
            templ2 = templates[k];
2325
            for (l = 0; l < templ2->templNr; l++) {
2326
                if (templ2->templCalledTab[l] == templ1) {
2327
                    snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2328
                    snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2329
                    snprintf(called_str,sizeof(called_str),"%6d/%d",
2330
                        templ2->templCountTab[l], /* number of times 'this' calls callee */
2331
                        total);                   /* total number of calls from 'this' */
2332
                    fprintf(output, "             %-8s %-8s %-12s     %s [%d]\n",
2333
                        times_str,timec_str,called_str,
2334
                        templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
2335
                }
2336
            }
2337
        }
2338
        fprintf(output, "-----------------------------------------------\n");
2339
    }
2340
2341
    fprintf(output, "\f\nIndex by function name\n");
2342
    for (i = 0; i < nb; i++) {
2343
        templ1 = templates[i];
2344
        fprintf(output, "[%d] %s (%s:%d)\n",
2345
            i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
2346
            templ1->style->doc->URL,templ1->elem->line);
2347
    }
2348
2349
    fprintf(output, "\f\n");
2350
    xmlFree(childt);
2351
2352
    xmlFree(templates);
2353
}
2354
2355
/************************************************************************
2356
 *                  *
2357
 *    Fetching profiling information        *
2358
 *                  *
2359
 ************************************************************************/
2360
2361
/**
2362
 * xsltGetProfileInformation:
2363
 * @ctxt:  a transformation context
2364
 *
2365
 * This function should be called after the transformation completed
2366
 * to extract template processing profiling information if available.
2367
 * The information is returned as an XML document tree like
2368
 * <?xml version="1.0"?>
2369
 * <profile>
2370
 * <template rank="1" match="*" name=""
2371
 *         mode="" calls="6" time="48" average="8"/>
2372
 * <template rank="2" match="item2|item3" name=""
2373
 *         mode="" calls="10" time="30" average="3"/>
2374
 * <template rank="3" match="item1" name=""
2375
 *         mode="" calls="5" time="17" average="3"/>
2376
 * </profile>
2377
 * The caller will need to free up the returned tree with xmlFreeDoc()
2378
 *
2379
 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2380
 */
2381
2382
xmlDocPtr
2383
xsltGetProfileInformation(xsltTransformContextPtr ctxt)
2384
{
2385
    xmlDocPtr ret = NULL;
2386
    xmlNodePtr root, child;
2387
    char buf[100];
2388
2389
    xsltStylesheetPtr style;
2390
    xsltTemplatePtr *templates;
2391
    xsltTemplatePtr templ;
2392
    int nb = 0, max = 0, i, j;
2393
2394
    if (!ctxt)
2395
        return NULL;
2396
2397
    if (!ctxt->profile)
2398
        return NULL;
2399
2400
    nb = 0;
2401
    max = 10000;
2402
    templates =
2403
        (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
2404
    if (templates == NULL)
2405
        return NULL;
2406
2407
    /*
2408
     * collect all the templates in an array
2409
     */
2410
    style = ctxt->style;
2411
    while (style != NULL) {
2412
        templ = style->templates;
2413
        while (templ != NULL) {
2414
            if (nb >= max)
2415
                break;
2416
2417
            if (templ->nbCalls > 0)
2418
                templates[nb++] = templ;
2419
            templ = templ->next;
2420
        }
2421
2422
        style = (xsltStylesheetPtr) xsltNextImport(style);
2423
    }
2424
2425
    /*
2426
     * Sort the array by time spent
2427
     */
2428
    for (i = 0; i < nb - 1; i++) {
2429
        for (j = i + 1; j < nb; j++) {
2430
            if ((templates[i]->time <= templates[j]->time) ||
2431
                ((templates[i]->time == templates[j]->time) &&
2432
                 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2433
                templ = templates[j];
2434
                templates[j] = templates[i];
2435
                templates[i] = templ;
2436
            }
2437
        }
2438
    }
2439
2440
    /*
2441
     * Generate a document corresponding to the results.
2442
     */
2443
    ret = xmlNewDoc(BAD_CAST "1.0");
2444
    root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
2445
    xmlDocSetRootElement(ret, root);
2446
2447
    for (i = 0; i < nb; i++) {
2448
        child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
2449
        snprintf(buf, sizeof(buf), "%d", i + 1);
2450
        xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
2451
        xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
2452
        xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
2453
        xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
2454
2455
        snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls);
2456
        xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
2457
2458
        snprintf(buf, sizeof(buf), "%ld", templates[i]->time);
2459
        xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
2460
2461
        snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls);
2462
        xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
2463
    };
2464
2465
    xmlFree(templates);
2466
2467
    return ret;
2468
}
2469
2470
#endif /* WITH_PROFILER */
2471
2472
/************************************************************************
2473
 *                  *
2474
 *    Hooks for libxml2 XPath         *
2475
 *                  *
2476
 ************************************************************************/
2477
2478
/**
2479
 * xsltXPathCompileFlags:
2480
 * @style: the stylesheet
2481
 * @str:  the XPath expression
2482
 * @flags: extra compilation flags to pass down to libxml2 XPath
2483
 *
2484
 * Compile an XPath expression
2485
 *
2486
 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2487
 *         the caller has to free the object.
2488
 */
2489
xmlXPathCompExprPtr
2490
0
xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
2491
0
    xmlXPathContextPtr xpathCtxt;
2492
0
    xmlXPathCompExprPtr ret;
2493
2494
0
    if (style != NULL) {
2495
0
        xpathCtxt = style->principal->xpathCtxt;
2496
0
  if (xpathCtxt == NULL)
2497
0
      return NULL;
2498
0
  xpathCtxt->dict = style->dict;
2499
0
    } else {
2500
0
  xpathCtxt = xmlXPathNewContext(NULL);
2501
0
  if (xpathCtxt == NULL)
2502
0
      return NULL;
2503
0
    }
2504
0
    xpathCtxt->flags = flags;
2505
2506
    /*
2507
    * Compile the expression.
2508
    */
2509
0
    ret = xmlXPathCtxtCompile(xpathCtxt, str);
2510
2511
0
    if (style == NULL) {
2512
0
  xmlXPathFreeContext(xpathCtxt);
2513
0
    }
2514
    /*
2515
     * TODO: there is a lot of optimizations which should be possible
2516
     *       like variable slot precomputations, function precomputations, etc.
2517
     */
2518
2519
0
    return(ret);
2520
0
}
2521
2522
/**
2523
 * xsltXPathCompile:
2524
 * @style: the stylesheet
2525
 * @str:  the XPath expression
2526
 *
2527
 * Compile an XPath expression
2528
 *
2529
 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2530
 *         the caller has to free the object.
2531
 */
2532
xmlXPathCompExprPtr
2533
0
xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
2534
0
    return(xsltXPathCompileFlags(style, str, 0));
2535
0
}
2536
2537
/************************************************************************
2538
 *                  *
2539
 *    Hooks for the debugger          *
2540
 *                  *
2541
 ************************************************************************/
2542
2543
int xslDebugStatus;
2544
2545
/**
2546
 * xsltGetDebuggerStatus:
2547
 *
2548
 * Get xslDebugStatus.
2549
 *
2550
 * Returns the value of xslDebugStatus.
2551
 */
2552
int
2553
xsltGetDebuggerStatus(void)
2554
0
{
2555
0
    return(xslDebugStatus);
2556
0
}
2557
2558
#ifdef WITH_DEBUGGER
2559
2560
/*
2561
 * There is currently only 3 debugging callback defined
2562
 * Debugger callbacks are disabled by default
2563
 */
2564
#define XSLT_CALLBACK_NUMBER 3
2565
2566
typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
2567
typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
2568
struct _xsltDebuggerCallbacks {
2569
    xsltHandleDebuggerCallback handler;
2570
    xsltAddCallCallback add;
2571
    xsltDropCallCallback drop;
2572
};
2573
2574
static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
2575
    NULL, /* handler */
2576
    NULL, /* add */
2577
    NULL  /* drop */
2578
};
2579
2580
/**
2581
 * xsltSetDebuggerStatus:
2582
 * @value : the value to be set
2583
 *
2584
 * This function sets the value of xslDebugStatus.
2585
 */
2586
void
2587
xsltSetDebuggerStatus(int value)
2588
{
2589
    xslDebugStatus = value;
2590
}
2591
2592
/**
2593
 * xsltSetDebuggerCallbacks:
2594
 * @no : number of callbacks
2595
 * @block : the block of callbacks
2596
 *
2597
 * This function allow to plug a debugger into the XSLT library
2598
 * @block points to a block of memory containing the address of @no
2599
 * callback routines.
2600
 *
2601
 * Returns 0 in case of success and -1 in case of error
2602
 */
2603
int
2604
xsltSetDebuggerCallbacks(int no, void *block)
2605
{
2606
    xsltDebuggerCallbacksPtr callbacks;
2607
2608
    if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2609
  return(-1);
2610
2611
    callbacks = (xsltDebuggerCallbacksPtr) block;
2612
    xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2613
    xsltDebuggerCurrentCallbacks.add  = callbacks->add;
2614
    xsltDebuggerCurrentCallbacks.drop  = callbacks->drop;
2615
    return(0);
2616
}
2617
2618
/**
2619
 * xslHandleDebugger:
2620
 * @cur : source node being executed
2621
 * @node : data node being processed
2622
 * @templ : temlate that applies to node
2623
 * @ctxt : the xslt transform context
2624
 *
2625
 * If either cur or node are a breakpoint, or xslDebugStatus in state
2626
 *   where debugging must occcur at this time then transfer control
2627
 *   to the xslDebugBreak function
2628
 */
2629
void
2630
xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2631
            xsltTransformContextPtr ctxt)
2632
{
2633
    if (xsltDebuggerCurrentCallbacks.handler != NULL)
2634
  xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2635
}
2636
2637
/**
2638
 * xslAddCall:
2639
 * @templ : current template being applied
2640
 * @source : the source node being processed
2641
 *
2642
 * Add template "call" to call stack
2643
 * Returns : 1 on sucess 0 otherwise an error may be printed if
2644
 *            WITH_XSLT_DEBUG_BREAKPOINTS is defined
2645
 */
2646
int
2647
xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2648
{
2649
    if (xsltDebuggerCurrentCallbacks.add != NULL)
2650
  return(xsltDebuggerCurrentCallbacks.add(templ, source));
2651
    return(0);
2652
}
2653
2654
/**
2655
 * xslDropCall:
2656
 *
2657
 * Drop the topmost item off the call stack
2658
 */
2659
void
2660
xslDropCall(void)
2661
{
2662
    if (xsltDebuggerCurrentCallbacks.drop != NULL)
2663
  xsltDebuggerCurrentCallbacks.drop();
2664
}
2665
2666
#endif /* WITH_DEBUGGER */
2667