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