Coverage Report

Created: 2025-08-26 07:08

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