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