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)
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 *
xsltGetCNsProp(xsltStylesheetPtr style,xmlNodePtr node,const xmlChar * name,const xmlChar * nameSpace)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 *
xsltGetNsProp(xmlNodePtr node,const xmlChar * name,const xmlChar * nameSpace)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
xsltGetUTF8Char(const unsigned char * utf,int * len)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
xsltPointerListAddSize(xsltPointerListPtr list,void * item,int initialSize)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
xsltPointerListCreate(int initialSize)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
xsltPointerListFree(xsltPointerListPtr list)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
xsltPointerListClear(xsltPointerListPtr list)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
xsltMessage(xsltTransformContextPtr ctxt,xmlNodePtr node,xmlNodePtr inst)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)
xsltGenericErrorDefaultFunc(void * ctx ATTRIBUTE_UNUSED,const char * msg,...)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
xsltSetGenericErrorFunc(void * ctx,xmlGenericErrorFunc handler)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)
xsltGenericDebugDefaultFunc(void * ctx ATTRIBUTE_UNUSED,const char * msg,...)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
xsltSetGenericDebugFunc(void * ctx,xmlGenericErrorFunc handler)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
xsltPrintErrorContext(xsltTransformContextPtr ctxt,xsltStylesheetPtr style,xmlNodePtr node)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
xsltSetTransformErrorFunc(xsltTransformContextPtr ctxt,void * ctx,xmlGenericErrorFunc handler)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
xsltTransformError(xsltTransformContextPtr ctxt,xsltStylesheetPtr style,xmlNodePtr node,const char * msg,...)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 *
xsltSplitQName(xmlDictPtr dict,const xmlChar * name,const xmlChar ** prefix)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 *
xsltGetQNameURI(xmlNodePtr node,xmlChar ** name)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 *
xsltGetQNameURI2(xsltStylesheetPtr style,xmlNodePtr node,const xmlChar ** name)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
xsltDocumentSortFunction(xmlNodeSetPtr list)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 * xsltComputeSortResultiInternal:
928 * @ctxt: a XSLT process context
929 * @sort: node list
930 * @xfrm: Transform strings according to locale
931 *
932 * reorder the current node list accordingly to the set of sorting
933 * requirement provided by the array of nodes.
934 *
935 * Returns a ordered XPath nodeset or NULL in case of error.
936 */
937 static xmlXPathObjectPtr *
xsltComputeSortResultInternal(xsltTransformContextPtr ctxt,xmlNodePtr sort,int xfrm)938 xsltComputeSortResultInternal(xsltTransformContextPtr ctxt, xmlNodePtr sort,
939 int xfrm) {
940 #ifdef XSLT_REFACTORED
941 xsltStyleItemSortPtr comp;
942 #else
943 xsltStylePreCompPtr comp;
944 #endif
945 xmlXPathObjectPtr *results = NULL;
946 xmlNodeSetPtr list = NULL;
947 xmlXPathObjectPtr res;
948 int len = 0;
949 int i;
950 xmlNodePtr oldNode;
951 xmlNodePtr oldInst;
952 int oldPos, oldSize ;
953 int oldNsNr;
954 xmlNsPtr *oldNamespaces;
955
956 comp = sort->psvi;
957 if (comp == NULL) {
958 xsltGenericError(xsltGenericErrorContext,
959 "xsl:sort : compilation failed\n");
960 return(NULL);
961 }
962
963 if ((comp->select == NULL) || (comp->comp == NULL))
964 return(NULL);
965
966 list = ctxt->nodeList;
967 if ((list == NULL) || (list->nodeNr <= 1))
968 return(NULL);
969
970 len = list->nodeNr;
971
972 /* TODO: xsl:sort lang attribute */
973 /* TODO: xsl:sort case-order attribute */
974
975
976 results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
977 if (results == NULL) {
978 xsltGenericError(xsltGenericErrorContext,
979 "xsltComputeSortResult: memory allocation failure\n");
980 return(NULL);
981 }
982
983 oldNode = ctxt->node;
984 oldInst = ctxt->inst;
985 oldPos = ctxt->xpathCtxt->proximityPosition;
986 oldSize = ctxt->xpathCtxt->contextSize;
987 oldNsNr = ctxt->xpathCtxt->nsNr;
988 oldNamespaces = ctxt->xpathCtxt->namespaces;
989 for (i = 0;i < len;i++) {
990 ctxt->inst = sort;
991 ctxt->xpathCtxt->contextSize = len;
992 ctxt->xpathCtxt->proximityPosition = i + 1;
993 ctxt->node = list->nodeTab[i];
994 ctxt->xpathCtxt->node = ctxt->node;
995 #ifdef XSLT_REFACTORED
996 if (comp->inScopeNs != NULL) {
997 ctxt->xpathCtxt->namespaces = comp->inScopeNs->list;
998 ctxt->xpathCtxt->nsNr = comp->inScopeNs->xpathNumber;
999 } else {
1000 ctxt->xpathCtxt->namespaces = NULL;
1001 ctxt->xpathCtxt->nsNr = 0;
1002 }
1003 #else
1004 ctxt->xpathCtxt->namespaces = comp->nsList;
1005 ctxt->xpathCtxt->nsNr = comp->nsNr;
1006 #endif
1007 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1008 if (res != NULL) {
1009 if (res->type != XPATH_STRING)
1010 res = xmlXPathConvertString(res);
1011 if (comp->number)
1012 res = xmlXPathConvertNumber(res);
1013 res->index = i; /* Save original pos for dupl resolv */
1014 if (comp->number) {
1015 if (res->type == XPATH_NUMBER) {
1016 results[i] = res;
1017 } else {
1018 #ifdef WITH_XSLT_DEBUG_PROCESS
1019 xsltGenericDebug(xsltGenericDebugContext,
1020 "xsltComputeSortResult: select didn't evaluate to a number\n");
1021 #endif
1022 results[i] = NULL;
1023 }
1024 } else {
1025 if (res->type == XPATH_STRING) {
1026 if ((xfrm) && (comp->locale != (xsltLocale)0)) {
1027 xmlChar *str = res->stringval;
1028 res->stringval = (xmlChar *) xsltStrxfrm(comp->locale, str);
1029 xmlFree(str);
1030 }
1031
1032 results[i] = res;
1033 } else {
1034 #ifdef WITH_XSLT_DEBUG_PROCESS
1035 xsltGenericDebug(xsltGenericDebugContext,
1036 "xsltComputeSortResult: select didn't evaluate to a string\n");
1037 #endif
1038 results[i] = NULL;
1039 }
1040 }
1041 } else {
1042 ctxt->state = XSLT_STATE_STOPPED;
1043 results[i] = NULL;
1044 }
1045 }
1046 ctxt->node = oldNode;
1047 ctxt->inst = oldInst;
1048 ctxt->xpathCtxt->contextSize = oldSize;
1049 ctxt->xpathCtxt->proximityPosition = oldPos;
1050 ctxt->xpathCtxt->nsNr = oldNsNr;
1051 ctxt->xpathCtxt->namespaces = oldNamespaces;
1052
1053 return(results);
1054 }
1055
1056 /**
1057 * xsltComputeSortResult:
1058 * @ctxt: a XSLT process context
1059 * @sort: node list
1060 *
1061 * reorder the current node list accordingly to the set of sorting
1062 * requirement provided by the array of nodes.
1063 *
1064 * Returns a ordered XPath nodeset or NULL in case of error.
1065 */
1066 xmlXPathObjectPtr *
xsltComputeSortResult(xsltTransformContextPtr ctxt,xmlNodePtr sort)1067 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
1068 return xsltComputeSortResultInternal(ctxt, sort, /* xfrm */ 0);
1069 }
1070
1071 /**
1072 * xsltDefaultSortFunction:
1073 * @ctxt: a XSLT process context
1074 * @sorts: array of sort nodes
1075 * @nbsorts: the number of sorts in the array
1076 *
1077 * reorder the current node list accordingly to the set of sorting
1078 * requirement provided by the arry of nodes.
1079 */
1080 void
xsltDefaultSortFunction(xsltTransformContextPtr ctxt,xmlNodePtr * sorts,int nbsorts)1081 xsltDefaultSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
1082 int nbsorts) {
1083 #ifdef XSLT_REFACTORED
1084 xsltStyleItemSortPtr comp;
1085 #else
1086 xsltStylePreCompPtr comp;
1087 #endif
1088 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
1089 xmlXPathObjectPtr *results = NULL, *res;
1090 xmlNodeSetPtr list = NULL;
1091 int descending, number, desc, numb;
1092 int len = 0;
1093 int i, j, incr;
1094 int tst;
1095 int depth;
1096 xmlNodePtr node;
1097 xmlXPathObjectPtr tmp;
1098 int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT],
1099 templang[XSLT_MAX_SORT];
1100
1101 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
1102 (nbsorts >= XSLT_MAX_SORT))
1103 return;
1104 if (sorts[0] == NULL)
1105 return;
1106 comp = sorts[0]->psvi;
1107 if (comp == NULL)
1108 return;
1109
1110 list = ctxt->nodeList;
1111 if ((list == NULL) || (list->nodeNr <= 1))
1112 return; /* nothing to do */
1113
1114 for (j = 0; j < nbsorts; j++) {
1115 comp = sorts[j]->psvi;
1116 tempstype[j] = 0;
1117 if ((comp->stype == NULL) && (comp->has_stype != 0)) {
1118 comp->stype =
1119 xsltEvalAttrValueTemplate(ctxt, sorts[j],
1120 (const xmlChar *) "data-type",
1121 NULL);
1122 if (comp->stype != NULL) {
1123 tempstype[j] = 1;
1124 if (xmlStrEqual(comp->stype, (const xmlChar *) "text"))
1125 comp->number = 0;
1126 else if (xmlStrEqual(comp->stype, (const xmlChar *) "number"))
1127 comp->number = 1;
1128 else {
1129 xsltTransformError(ctxt, NULL, sorts[j],
1130 "xsltDoSortFunction: no support for data-type = %s\n",
1131 comp->stype);
1132 comp->number = 0; /* use default */
1133 }
1134 }
1135 }
1136 temporder[j] = 0;
1137 if ((comp->order == NULL) && (comp->has_order != 0)) {
1138 comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1139 (const xmlChar *) "order",
1140 NULL);
1141 if (comp->order != NULL) {
1142 temporder[j] = 1;
1143 if (xmlStrEqual(comp->order, (const xmlChar *) "ascending"))
1144 comp->descending = 0;
1145 else if (xmlStrEqual(comp->order,
1146 (const xmlChar *) "descending"))
1147 comp->descending = 1;
1148 else {
1149 xsltTransformError(ctxt, NULL, sorts[j],
1150 "xsltDoSortFunction: invalid value %s for order\n",
1151 comp->order);
1152 comp->descending = 0; /* use default */
1153 }
1154 }
1155 }
1156 templang[j] = 0;
1157 if ((comp->lang == NULL) && (comp->has_lang != 0)) {
1158 xmlChar *lang = xsltEvalAttrValueTemplate(ctxt, sorts[j],
1159 (xmlChar *) "lang",
1160 NULL);
1161 if (lang != NULL) {
1162 templang[j] = 1;
1163 comp->locale = xsltNewLocale(lang);
1164 xmlFree(lang);
1165 }
1166 }
1167 }
1168
1169 len = list->nodeNr;
1170
1171 resultsTab[0] = xsltComputeSortResultInternal(ctxt, sorts[0],
1172 /* xfrm */ 1);
1173 for (i = 1;i < XSLT_MAX_SORT;i++)
1174 resultsTab[i] = NULL;
1175
1176 results = resultsTab[0];
1177
1178 comp = sorts[0]->psvi;
1179 descending = comp->descending;
1180 number = comp->number;
1181 if (results == NULL)
1182 goto cleanup;
1183
1184 /* Shell's sort of node-set */
1185 for (incr = len / 2; incr > 0; incr /= 2) {
1186 for (i = incr; i < len; i++) {
1187 j = i - incr;
1188 if (results[i] == NULL)
1189 continue;
1190
1191 while (j >= 0) {
1192 if (results[j] == NULL)
1193 tst = 1;
1194 else {
1195 if (number) {
1196 /* We make NaN smaller than number in accordance
1197 with XSLT spec */
1198 if (xmlXPathIsNaN(results[j]->floatval)) {
1199 if (xmlXPathIsNaN(results[j + incr]->floatval))
1200 tst = 0;
1201 else
1202 tst = -1;
1203 } else if (xmlXPathIsNaN(results[j + incr]->floatval))
1204 tst = 1;
1205 else if (results[j]->floatval ==
1206 results[j + incr]->floatval)
1207 tst = 0;
1208 else if (results[j]->floatval >
1209 results[j + incr]->floatval)
1210 tst = 1;
1211 else tst = -1;
1212 } else if(comp->locale != (xsltLocale)0) {
1213 tst = xsltLocaleStrcmp(
1214 comp->locale,
1215 (xsltLocaleChar *) results[j]->stringval,
1216 (xsltLocaleChar *) results[j + incr]->stringval);
1217 } else {
1218 tst = xmlStrcmp(results[j]->stringval,
1219 results[j + incr]->stringval);
1220 }
1221 if (descending)
1222 tst = -tst;
1223 }
1224 if (tst == 0) {
1225 /*
1226 * Okay we need to use multi level sorts
1227 */
1228 depth = 1;
1229 while (depth < nbsorts) {
1230 if (sorts[depth] == NULL)
1231 break;
1232 comp = sorts[depth]->psvi;
1233 if (comp == NULL)
1234 break;
1235 desc = comp->descending;
1236 numb = comp->number;
1237
1238 /*
1239 * Compute the result of the next level for the
1240 * full set, this might be optimized ... or not
1241 */
1242 if (resultsTab[depth] == NULL)
1243 resultsTab[depth] =
1244 xsltComputeSortResultInternal(ctxt,
1245 sorts[depth],
1246 /* xfrm */ 1);
1247 res = resultsTab[depth];
1248 if (res == NULL)
1249 break;
1250 if (res[j] == NULL) {
1251 if (res[j+incr] != NULL)
1252 tst = 1;
1253 } else if (res[j+incr] == NULL) {
1254 tst = -1;
1255 } else {
1256 if (numb) {
1257 /* We make NaN smaller than number in
1258 accordance with XSLT spec */
1259 if (xmlXPathIsNaN(res[j]->floatval)) {
1260 if (xmlXPathIsNaN(res[j +
1261 incr]->floatval))
1262 tst = 0;
1263 else
1264 tst = -1;
1265 } else if (xmlXPathIsNaN(res[j + incr]->
1266 floatval))
1267 tst = 1;
1268 else if (res[j]->floatval == res[j + incr]->
1269 floatval)
1270 tst = 0;
1271 else if (res[j]->floatval >
1272 res[j + incr]->floatval)
1273 tst = 1;
1274 else tst = -1;
1275 } else if(comp->locale != (xsltLocale)0) {
1276 tst = xsltLocaleStrcmp(
1277 comp->locale,
1278 (xsltLocaleChar *) res[j]->stringval,
1279 (xsltLocaleChar *) res[j + incr]->stringval);
1280 } else {
1281 tst = xmlStrcmp(res[j]->stringval,
1282 res[j + incr]->stringval);
1283 }
1284 if (desc)
1285 tst = -tst;
1286 }
1287
1288 /*
1289 * if we still can't differenciate at this level
1290 * try one level deeper.
1291 */
1292 if (tst != 0)
1293 break;
1294 depth++;
1295 }
1296 }
1297 if (tst == 0) {
1298 tst = results[j]->index > results[j + incr]->index;
1299 }
1300 if (tst > 0) {
1301 tmp = results[j];
1302 results[j] = results[j + incr];
1303 results[j + incr] = tmp;
1304 node = list->nodeTab[j];
1305 list->nodeTab[j] = list->nodeTab[j + incr];
1306 list->nodeTab[j + incr] = node;
1307 depth = 1;
1308 while (depth < nbsorts) {
1309 if (sorts[depth] == NULL)
1310 break;
1311 if (resultsTab[depth] == NULL)
1312 break;
1313 res = resultsTab[depth];
1314 tmp = res[j];
1315 res[j] = res[j + incr];
1316 res[j + incr] = tmp;
1317 depth++;
1318 }
1319 j -= incr;
1320 } else
1321 break;
1322 }
1323 }
1324 }
1325
1326 cleanup:
1327 for (j = 0; j < nbsorts; j++) {
1328 comp = sorts[j]->psvi;
1329 if (tempstype[j] == 1) {
1330 /* The data-type needs to be recomputed each time */
1331 xmlFree((void *)(comp->stype));
1332 comp->stype = NULL;
1333 }
1334 if (temporder[j] == 1) {
1335 /* The order needs to be recomputed each time */
1336 xmlFree((void *)(comp->order));
1337 comp->order = NULL;
1338 }
1339 if (templang[j] == 1) {
1340 xsltFreeLocale(comp->locale);
1341 comp->locale = (xsltLocale)0;
1342 }
1343 if (resultsTab[j] != NULL) {
1344 for (i = 0;i < len;i++)
1345 xmlXPathFreeObject(resultsTab[j][i]);
1346 xmlFree(resultsTab[j]);
1347 }
1348 }
1349 }
1350
1351
1352 static xsltSortFunc xsltSortFunction = xsltDefaultSortFunction;
1353
1354 /**
1355 * xsltDoSortFunction:
1356 * @ctxt: a XSLT process context
1357 * @sorts: array of sort nodes
1358 * @nbsorts: the number of sorts in the array
1359 *
1360 * reorder the current node list accordingly to the set of sorting
1361 * requirement provided by the arry of nodes.
1362 * This is a wrapper function, the actual function used is specified
1363 * using xsltSetCtxtSortFunc() to set the context specific sort function,
1364 * or xsltSetSortFunc() to set the global sort function.
1365 * If a sort function is set on the context, this will get called.
1366 * Otherwise the global sort function is called.
1367 */
1368 void
xsltDoSortFunction(xsltTransformContextPtr ctxt,xmlNodePtr * sorts,int nbsorts)1369 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr * sorts,
1370 int nbsorts)
1371 {
1372 if (ctxt->sortfunc != NULL)
1373 (ctxt->sortfunc)(ctxt, sorts, nbsorts);
1374 else if (xsltSortFunction != NULL)
1375 xsltSortFunction(ctxt, sorts, nbsorts);
1376 }
1377
1378 /**
1379 * xsltSetSortFunc:
1380 * @handler: the new handler function
1381 *
1382 * Function to reset the global handler for XSLT sorting.
1383 * If the handler is NULL, the default sort function will be used.
1384 */
1385 void
xsltSetSortFunc(xsltSortFunc handler)1386 xsltSetSortFunc(xsltSortFunc handler) {
1387 if (handler != NULL)
1388 xsltSortFunction = handler;
1389 else
1390 xsltSortFunction = xsltDefaultSortFunction;
1391 }
1392
1393 /**
1394 * xsltSetCtxtSortFunc:
1395 * @ctxt: a XSLT process context
1396 * @handler: the new handler function
1397 *
1398 * Function to set the handler for XSLT sorting
1399 * for the specified context.
1400 * If the handler is NULL, then the global
1401 * sort function will be called
1402 */
1403 void
xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt,xsltSortFunc handler)1404 xsltSetCtxtSortFunc(xsltTransformContextPtr ctxt, xsltSortFunc handler) {
1405 ctxt->sortfunc = handler;
1406 }
1407
1408 /************************************************************************
1409 * *
1410 * Parsing options *
1411 * *
1412 ************************************************************************/
1413
1414 /**
1415 * xsltSetCtxtParseOptions:
1416 * @ctxt: a XSLT process context
1417 * @options: a combination of libxml2 xmlParserOption
1418 *
1419 * Change the default parser option passed by the XSLT engine to the
1420 * parser when using document() loading.
1421 *
1422 * Returns the previous options or -1 in case of error
1423 */
1424 int
xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt,int options)1425 xsltSetCtxtParseOptions(xsltTransformContextPtr ctxt, int options)
1426 {
1427 int oldopts;
1428
1429 if (ctxt == NULL)
1430 return(-1);
1431 oldopts = ctxt->parserOptions;
1432 if (ctxt->xinclude)
1433 oldopts |= XML_PARSE_XINCLUDE;
1434 ctxt->parserOptions = options;
1435 if (options & XML_PARSE_XINCLUDE)
1436 ctxt->xinclude = 1;
1437 else
1438 ctxt->xinclude = 0;
1439 return(oldopts);
1440 }
1441
1442 /************************************************************************
1443 * *
1444 * Output *
1445 * *
1446 ************************************************************************/
1447
1448 /**
1449 * xsltSaveResultTo:
1450 * @buf: an output buffer
1451 * @result: the result xmlDocPtr
1452 * @style: the stylesheet
1453 *
1454 * Save the result @result obtained by applying the @style stylesheet
1455 * to an I/O output channel @buf
1456 *
1457 * Returns the number of byte written or -1 in case of failure.
1458 */
1459 int
xsltSaveResultTo(xmlOutputBufferPtr buf,xmlDocPtr result,xsltStylesheetPtr style)1460 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
1461 xsltStylesheetPtr style) {
1462 const xmlChar *encoding;
1463 int base;
1464 const xmlChar *method;
1465 int indent;
1466
1467 if ((buf == NULL) || (result == NULL) || (style == NULL))
1468 return(-1);
1469 if ((result->children == NULL) ||
1470 ((result->children->type == XML_DTD_NODE) &&
1471 (result->children->next == NULL)))
1472 return(0);
1473
1474 if ((style->methodURI != NULL) &&
1475 ((style->method == NULL) ||
1476 (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
1477 xsltGenericError(xsltGenericErrorContext,
1478 "xsltSaveResultTo : unknown output method\n");
1479 return(-1);
1480 }
1481
1482 base = buf->written;
1483
1484 XSLT_GET_IMPORT_PTR(method, style, method)
1485 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1486 XSLT_GET_IMPORT_INT(indent, style, indent);
1487
1488 if ((method == NULL) && (result->type == XML_HTML_DOCUMENT_NODE))
1489 method = (const xmlChar *) "html";
1490
1491 if ((method != NULL) &&
1492 (xmlStrEqual(method, (const xmlChar *) "html"))) {
1493 if (encoding != NULL) {
1494 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1495 } else {
1496 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1497 }
1498 if (indent == -1)
1499 indent = 1;
1500 htmlDocContentDumpFormatOutput(buf, result, (const char *) encoding,
1501 indent);
1502 xmlOutputBufferFlush(buf);
1503 } else if ((method != NULL) &&
1504 (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
1505 if (encoding != NULL) {
1506 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1507 } else {
1508 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
1509 }
1510 htmlDocContentDumpOutput(buf, result, (const char *) encoding);
1511 xmlOutputBufferFlush(buf);
1512 } else if ((method != NULL) &&
1513 (xmlStrEqual(method, (const xmlChar *) "text"))) {
1514 xmlNodePtr cur;
1515
1516 cur = result->children;
1517 while (cur != NULL) {
1518 if (cur->type == XML_TEXT_NODE)
1519 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1520
1521 /*
1522 * Skip to next node
1523 */
1524 if (cur->children != NULL) {
1525 if ((cur->children->type != XML_ENTITY_DECL) &&
1526 (cur->children->type != XML_ENTITY_REF_NODE) &&
1527 (cur->children->type != XML_ENTITY_NODE)) {
1528 cur = cur->children;
1529 continue;
1530 }
1531 }
1532 if (cur->next != NULL) {
1533 cur = cur->next;
1534 continue;
1535 }
1536
1537 do {
1538 cur = cur->parent;
1539 if (cur == NULL)
1540 break;
1541 if (cur == (xmlNodePtr) style->doc) {
1542 cur = NULL;
1543 break;
1544 }
1545 if (cur->next != NULL) {
1546 cur = cur->next;
1547 break;
1548 }
1549 } while (cur != NULL);
1550 }
1551 xmlOutputBufferFlush(buf);
1552 } else {
1553 int omitXmlDecl;
1554 int standalone;
1555
1556 XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
1557 XSLT_GET_IMPORT_INT(standalone, style, standalone);
1558
1559 if (omitXmlDecl != 1) {
1560 xmlOutputBufferWriteString(buf, "<?xml version=");
1561 if (result->version != NULL) {
1562 xmlOutputBufferWriteString(buf, "\"");
1563 xmlOutputBufferWriteString(buf, (const char *)result->version);
1564 xmlOutputBufferWriteString(buf, "\"");
1565 } else
1566 xmlOutputBufferWriteString(buf, "\"1.0\"");
1567 if (encoding == NULL) {
1568 if (result->encoding != NULL)
1569 encoding = result->encoding;
1570 else if (result->charset != XML_CHAR_ENCODING_UTF8)
1571 encoding = (const xmlChar *)
1572 xmlGetCharEncodingName((xmlCharEncoding)
1573 result->charset);
1574 }
1575 if (encoding != NULL) {
1576 xmlOutputBufferWriteString(buf, " encoding=");
1577 xmlOutputBufferWriteString(buf, "\"");
1578 xmlOutputBufferWriteString(buf, (const char *) encoding);
1579 xmlOutputBufferWriteString(buf, "\"");
1580 }
1581 switch (standalone) {
1582 case 0:
1583 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
1584 break;
1585 case 1:
1586 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
1587 break;
1588 default:
1589 break;
1590 }
1591 xmlOutputBufferWriteString(buf, "?>\n");
1592 }
1593 if (result->children != NULL) {
1594 xmlNodePtr children = result->children;
1595 xmlNodePtr child = children;
1596
1597 /*
1598 * Hack to avoid quadratic behavior when scanning
1599 * result->children in xmlGetIntSubset called by
1600 * xmlNodeDumpOutput.
1601 */
1602 result->children = NULL;
1603
1604 while (child != NULL) {
1605 xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
1606 (const char *) encoding);
1607 if (indent && ((child->type == XML_DTD_NODE) ||
1608 ((child->type == XML_COMMENT_NODE) &&
1609 (child->next != NULL))))
1610 xmlOutputBufferWriteString(buf, "\n");
1611 child = child->next;
1612 }
1613 if (indent)
1614 xmlOutputBufferWriteString(buf, "\n");
1615
1616 result->children = children;
1617 }
1618 xmlOutputBufferFlush(buf);
1619 }
1620 return(buf->written - base);
1621 }
1622
1623 /**
1624 * xsltSaveResultToFilename:
1625 * @URL: a filename or URL
1626 * @result: the result xmlDocPtr
1627 * @style: the stylesheet
1628 * @compression: the compression factor (0 - 9 included)
1629 *
1630 * Save the result @result obtained by applying the @style stylesheet
1631 * to a file or @URL
1632 *
1633 * Returns the number of byte written or -1 in case of failure.
1634 */
1635 int
xsltSaveResultToFilename(const char * URL,xmlDocPtr result,xsltStylesheetPtr style,int compression)1636 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
1637 xsltStylesheetPtr style, int compression) {
1638 xmlOutputBufferPtr buf;
1639 const xmlChar *encoding;
1640 int ret;
1641
1642 if ((URL == 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 = xmlOutputBufferCreateFilename(URL, encoder, compression);
1657 } else {
1658 buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
1659 }
1660 if (buf == NULL)
1661 return(-1);
1662 xsltSaveResultTo(buf, result, style);
1663 ret = xmlOutputBufferClose(buf);
1664 return(ret);
1665 }
1666
1667 /**
1668 * xsltSaveResultToFile:
1669 * @file: a FILE * I/O
1670 * @result: the result xmlDocPtr
1671 * @style: the stylesheet
1672 *
1673 * Save the result @result obtained by applying the @style stylesheet
1674 * to an open FILE * I/O.
1675 * This does not close the FILE @file
1676 *
1677 * Returns the number of bytes written or -1 in case of failure.
1678 */
1679 int
xsltSaveResultToFile(FILE * file,xmlDocPtr result,xsltStylesheetPtr style)1680 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
1681 xmlOutputBufferPtr buf;
1682 const xmlChar *encoding;
1683 int ret;
1684
1685 if ((file == NULL) || (result == NULL) || (style == NULL))
1686 return(-1);
1687 if (result->children == NULL)
1688 return(0);
1689
1690 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1691 if (encoding != NULL) {
1692 xmlCharEncodingHandlerPtr encoder;
1693
1694 encoder = xmlFindCharEncodingHandler((char *)encoding);
1695 if ((encoder != NULL) &&
1696 (xmlStrEqual((const xmlChar *)encoder->name,
1697 (const xmlChar *) "UTF-8")))
1698 encoder = NULL;
1699 buf = xmlOutputBufferCreateFile(file, encoder);
1700 } else {
1701 buf = xmlOutputBufferCreateFile(file, NULL);
1702 }
1703
1704 if (buf == NULL)
1705 return(-1);
1706 xsltSaveResultTo(buf, result, style);
1707 ret = xmlOutputBufferClose(buf);
1708 return(ret);
1709 }
1710
1711 /**
1712 * xsltSaveResultToFd:
1713 * @fd: a file descriptor
1714 * @result: the result xmlDocPtr
1715 * @style: the stylesheet
1716 *
1717 * Save the result @result obtained by applying the @style stylesheet
1718 * to an open file descriptor
1719 * This does not close the descriptor.
1720 *
1721 * Returns the number of bytes written or -1 in case of failure.
1722 */
1723 int
xsltSaveResultToFd(int fd,xmlDocPtr result,xsltStylesheetPtr style)1724 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
1725 xmlOutputBufferPtr buf;
1726 const xmlChar *encoding;
1727 int ret;
1728
1729 if ((fd < 0) || (result == NULL) || (style == NULL))
1730 return(-1);
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 = xmlOutputBufferCreateFd(fd, encoder);
1744 } else {
1745 buf = xmlOutputBufferCreateFd(fd, NULL);
1746 }
1747 if (buf == NULL)
1748 return(-1);
1749 xsltSaveResultTo(buf, result, style);
1750 ret = xmlOutputBufferClose(buf);
1751 return(ret);
1752 }
1753
1754 /**
1755 * xsltSaveResultToString:
1756 * @doc_txt_ptr: Memory pointer for allocated XML text
1757 * @doc_txt_len: Length of the generated XML text
1758 * @result: the result xmlDocPtr
1759 * @style: the stylesheet
1760 *
1761 * Save the result @result obtained by applying the @style stylesheet
1762 * to a new allocated string.
1763 *
1764 * Returns 0 in case of success and -1 in case of error
1765 */
1766 int
xsltSaveResultToString(xmlChar ** doc_txt_ptr,int * doc_txt_len,xmlDocPtr result,xsltStylesheetPtr style)1767 xsltSaveResultToString(xmlChar **doc_txt_ptr, int * doc_txt_len,
1768 xmlDocPtr result, xsltStylesheetPtr style) {
1769 xmlOutputBufferPtr buf;
1770 const xmlChar *encoding;
1771
1772 *doc_txt_ptr = NULL;
1773 *doc_txt_len = 0;
1774 if (result->children == NULL)
1775 return(0);
1776
1777 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
1778 if (encoding != NULL) {
1779 xmlCharEncodingHandlerPtr encoder;
1780
1781 encoder = xmlFindCharEncodingHandler((char *)encoding);
1782 if ((encoder != NULL) &&
1783 (xmlStrEqual((const xmlChar *)encoder->name,
1784 (const xmlChar *) "UTF-8")))
1785 encoder = NULL;
1786 buf = xmlAllocOutputBuffer(encoder);
1787 } else {
1788 buf = xmlAllocOutputBuffer(NULL);
1789 }
1790 if (buf == NULL)
1791 return(-1);
1792 xsltSaveResultTo(buf, result, style);
1793 #ifdef LIBXML2_NEW_BUFFER
1794 if (buf->conv != NULL) {
1795 *doc_txt_len = xmlBufUse(buf->conv);
1796 *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->conv), *doc_txt_len);
1797 } else {
1798 *doc_txt_len = xmlBufUse(buf->buffer);
1799 *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), *doc_txt_len);
1800 }
1801 #else
1802 if (buf->conv != NULL) {
1803 *doc_txt_len = buf->conv->use;
1804 *doc_txt_ptr = xmlStrndup(buf->conv->content, *doc_txt_len);
1805 } else {
1806 *doc_txt_len = buf->buffer->use;
1807 *doc_txt_ptr = xmlStrndup(buf->buffer->content, *doc_txt_len);
1808 }
1809 #endif
1810 (void)xmlOutputBufferClose(buf);
1811 return 0;
1812 }
1813
1814 #ifdef WITH_PROFILER
1815
1816 /************************************************************************
1817 * *
1818 * Generating profiling information *
1819 * *
1820 ************************************************************************/
1821
1822 static long calibration = -1;
1823
1824 /**
1825 * xsltCalibrateTimestamps:
1826 *
1827 * Used for to calibrate the xsltTimestamp() function
1828 * Should work if launched at startup and we don't loose our quantum :-)
1829 *
1830 * Returns the number of milliseconds used by xsltTimestamp()
1831 */
1832 #if !defined(XSLT_WIN32_PERFORMANCE_COUNTER) && \
1833 (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY))
1834 static long
xsltCalibrateTimestamps(void)1835 xsltCalibrateTimestamps(void) {
1836 register int i;
1837
1838 for (i = 0;i < 999;i++)
1839 xsltTimestamp();
1840 return(xsltTimestamp() / 1000);
1841 }
1842 #endif
1843
1844 /**
1845 * xsltCalibrateAdjust:
1846 * @delta: a negative dealy value found
1847 *
1848 * Used for to correct the calibration for xsltTimestamp()
1849 */
1850 void
xsltCalibrateAdjust(long delta)1851 xsltCalibrateAdjust(long delta) {
1852 calibration += delta;
1853 }
1854
1855 /**
1856 * xsltTimestamp:
1857 *
1858 * Used for gathering profiling data
1859 *
1860 * Returns the number of tenth of milliseconds since the beginning of the
1861 * profiling
1862 */
1863 long
xsltTimestamp(void)1864 xsltTimestamp(void)
1865 {
1866 #ifdef XSLT_WIN32_PERFORMANCE_COUNTER
1867 BOOL ok;
1868 LARGE_INTEGER performanceCount;
1869 LARGE_INTEGER performanceFrequency;
1870 LONGLONG quadCount;
1871 double seconds;
1872 static LONGLONG startupQuadCount = 0;
1873 static LONGLONG startupQuadFreq = 0;
1874
1875 ok = QueryPerformanceCounter(&performanceCount);
1876 if (!ok)
1877 return 0;
1878 quadCount = performanceCount.QuadPart;
1879 if (calibration < 0) {
1880 calibration = 0;
1881 ok = QueryPerformanceFrequency(&performanceFrequency);
1882 if (!ok)
1883 return 0;
1884 startupQuadFreq = performanceFrequency.QuadPart;
1885 startupQuadCount = quadCount;
1886 return (0);
1887 }
1888 if (startupQuadFreq == 0)
1889 return 0;
1890 seconds = (quadCount - startupQuadCount) / (double) startupQuadFreq;
1891 return (long) (seconds * XSLT_TIMESTAMP_TICS_PER_SEC);
1892
1893 #else /* XSLT_WIN32_PERFORMANCE_COUNTER */
1894 #ifdef HAVE_CLOCK_GETTIME
1895 # if defined(CLOCK_MONOTONIC)
1896 # define XSLT_CLOCK CLOCK_MONOTONIC
1897 # elif defined(CLOCK_HIGHRES)
1898 # define XSLT_CLOCK CLOCK_HIGHRES
1899 # else
1900 # define XSLT_CLOCK CLOCK_REALTIME
1901 # endif
1902 static struct timespec startup;
1903 struct timespec cur;
1904 long tics;
1905
1906 if (calibration < 0) {
1907 clock_gettime(XSLT_CLOCK, &startup);
1908 calibration = 0;
1909 calibration = xsltCalibrateTimestamps();
1910 clock_gettime(XSLT_CLOCK, &startup);
1911 return (0);
1912 }
1913
1914 clock_gettime(XSLT_CLOCK, &cur);
1915 tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1916 tics += (cur.tv_nsec - startup.tv_nsec) /
1917 (1000000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1918
1919 tics -= calibration;
1920 return(tics);
1921
1922 #elif HAVE_GETTIMEOFDAY
1923 static struct timeval startup;
1924 struct timeval cur;
1925 long tics;
1926
1927 if (calibration < 0) {
1928 gettimeofday(&startup, NULL);
1929 calibration = 0;
1930 calibration = xsltCalibrateTimestamps();
1931 gettimeofday(&startup, NULL);
1932 return (0);
1933 }
1934
1935 gettimeofday(&cur, NULL);
1936 tics = (cur.tv_sec - startup.tv_sec) * XSLT_TIMESTAMP_TICS_PER_SEC;
1937 tics += (cur.tv_usec - startup.tv_usec) /
1938 (1000000l / XSLT_TIMESTAMP_TICS_PER_SEC);
1939
1940 tics -= calibration;
1941 return(tics);
1942 #else
1943
1944 /* Neither gettimeofday() nor Win32 performance counter available */
1945
1946 return (0);
1947
1948 #endif /* HAVE_GETTIMEOFDAY */
1949 #endif /* XSLT_WIN32_PERFORMANCE_COUNTER */
1950 }
1951
1952 static char *
pretty_templ_match(xsltTemplatePtr templ)1953 pretty_templ_match(xsltTemplatePtr templ) {
1954 static char dst[1001];
1955 char *src = (char *)templ->match;
1956 int i=0,j;
1957
1958 /* strip white spaces */
1959 for (j=0; i<1000 && src[j]; i++,j++) {
1960 for(;src[j]==' ';j++);
1961 dst[i]=src[j];
1962 }
1963 if(i<998 && templ->mode) {
1964 /* append [mode] */
1965 dst[i++]='[';
1966 src=(char *)templ->mode;
1967 for (j=0; i<999 && src[j]; i++,j++) {
1968 dst[i]=src[j];
1969 }
1970 dst[i++]=']';
1971 }
1972 dst[i]='\0';
1973 return dst;
1974 }
1975
1976 #define MAX_TEMPLATES 10000
1977
1978 /**
1979 * xsltSaveProfiling:
1980 * @ctxt: an XSLT context
1981 * @output: a FILE * for saving the information
1982 *
1983 * Save the profiling information on @output
1984 */
1985 void
xsltSaveProfiling(xsltTransformContextPtr ctxt,FILE * output)1986 xsltSaveProfiling(xsltTransformContextPtr ctxt, FILE *output) {
1987 int nb, i,j,k,l;
1988 int max;
1989 int total;
1990 unsigned long totalt;
1991 xsltTemplatePtr *templates;
1992 xsltStylesheetPtr style;
1993 xsltTemplatePtr templ1,templ2;
1994 int *childt;
1995
1996 if ((output == NULL) || (ctxt == NULL))
1997 return;
1998 if (ctxt->profile == 0)
1999 return;
2000
2001 nb = 0;
2002 max = MAX_TEMPLATES;
2003 templates = xmlMalloc(max * sizeof(xsltTemplatePtr));
2004 if (templates == NULL)
2005 return;
2006
2007 style = ctxt->style;
2008 while (style != NULL) {
2009 templ1 = style->templates;
2010 while (templ1 != NULL) {
2011 if (nb >= max)
2012 break;
2013
2014 if (templ1->nbCalls > 0)
2015 templates[nb++] = templ1;
2016 templ1 = templ1->next;
2017 }
2018
2019 style = xsltNextImport(style);
2020 }
2021
2022 for (i = 0;i < nb -1;i++) {
2023 for (j = i + 1; j < nb; j++) {
2024 if ((templates[i]->time <= templates[j]->time) ||
2025 ((templates[i]->time == templates[j]->time) &&
2026 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2027 templ1 = templates[j];
2028 templates[j] = templates[i];
2029 templates[i] = templ1;
2030 }
2031 }
2032 }
2033
2034
2035 /* print flat profile */
2036
2037 fprintf(output, "%6s%20s%20s%10s Calls Tot 100us Avg\n\n",
2038 "number", "match", "name", "mode");
2039 total = 0;
2040 totalt = 0;
2041 for (i = 0;i < nb;i++) {
2042 templ1 = templates[i];
2043 fprintf(output, "%5d ", i);
2044 if (templ1->match != NULL) {
2045 if (xmlStrlen(templ1->match) > 20)
2046 fprintf(output, "%s\n%26s", templ1->match, "");
2047 else
2048 fprintf(output, "%20s", templ1->match);
2049 } else {
2050 fprintf(output, "%20s", "");
2051 }
2052 if (templ1->name != NULL) {
2053 if (xmlStrlen(templ1->name) > 20)
2054 fprintf(output, "%s\n%46s", templ1->name, "");
2055 else
2056 fprintf(output, "%20s", templ1->name);
2057 } else {
2058 fprintf(output, "%20s", "");
2059 }
2060 if (templ1->mode != NULL) {
2061 if (xmlStrlen(templ1->mode) > 10)
2062 fprintf(output, "%s\n%56s", templ1->mode, "");
2063 else
2064 fprintf(output, "%10s", templ1->mode);
2065 } else {
2066 fprintf(output, "%10s", "");
2067 }
2068 fprintf(output, " %6d", templ1->nbCalls);
2069 fprintf(output, " %6ld %6ld\n", templ1->time,
2070 templ1->time / templ1->nbCalls);
2071 total += templ1->nbCalls;
2072 totalt += templ1->time;
2073 }
2074 fprintf(output, "\n%30s%26s %6d %6ld\n", "Total", "", total, totalt);
2075
2076
2077 /* print call graph */
2078
2079 childt = xmlMalloc((nb + 1) * sizeof(int));
2080 if (childt == NULL)
2081 return;
2082
2083 /* precalculate children times */
2084 for (i = 0; i < nb; i++) {
2085 templ1 = templates[i];
2086
2087 childt[i] = 0;
2088 for (k = 0; k < nb; k++) {
2089 templ2 = templates[k];
2090 for (l = 0; l < templ2->templNr; l++) {
2091 if (templ2->templCalledTab[l] == templ1) {
2092 childt[i] +=templ2->time;
2093 }
2094 }
2095 }
2096 }
2097 childt[i] = 0;
2098
2099 fprintf(output, "\nindex %% time self children called name\n");
2100
2101 for (i = 0; i < nb; i++) {
2102 char ix_str[20], timep_str[20], times_str[20], timec_str[20], called_str[20];
2103 unsigned long t;
2104
2105 templ1 = templates[i];
2106 /* callers */
2107 for (j = 0; j < templ1->templNr; j++) {
2108 templ2 = templ1->templCalledTab[j];
2109 for (k = 0; k < nb; k++) {
2110 if (templates[k] == templ2)
2111 break;
2112 }
2113 t=templ2?templ2->time:totalt;
2114 snprintf(times_str,sizeof(times_str),"%8.3f",(float)t/XSLT_TIMESTAMP_TICS_PER_SEC);
2115 snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2116 snprintf(called_str,sizeof(called_str),"%6d/%d",
2117 templ1->templCountTab[j], /* number of times caller calls 'this' */
2118 templ1->nbCalls); /* total number of calls to 'this' */
2119
2120 fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2121 times_str,timec_str,called_str,
2122 (templ2?(templ2->name?(char *)templ2->name:pretty_templ_match(templ2)):"-"),k);
2123 }
2124 /* this */
2125 snprintf(ix_str,sizeof(ix_str),"[%d]",i);
2126 snprintf(timep_str,sizeof(timep_str),"%6.2f",(float)templ1->time*100.0/totalt);
2127 snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ1->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2128 snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[i]/XSLT_TIMESTAMP_TICS_PER_SEC);
2129 fprintf(output, "%-5s %-6s %-8s %-8s %6d %s [%d]\n",
2130 ix_str, timep_str,times_str,timec_str,
2131 templ1->nbCalls,
2132 templ1->name?(char *)templ1->name:pretty_templ_match(templ1),i);
2133 /* callees
2134 * - go over templates[0..nb] and their templCalledTab[]
2135 * - print those where we in the the call-stack
2136 */
2137 total = 0;
2138 for (k = 0; k < nb; k++) {
2139 templ2 = templates[k];
2140 for (l = 0; l < templ2->templNr; l++) {
2141 if (templ2->templCalledTab[l] == templ1) {
2142 total+=templ2->templCountTab[l];
2143 }
2144 }
2145 }
2146 for (k = 0; k < nb; k++) {
2147 templ2 = templates[k];
2148 for (l = 0; l < templ2->templNr; l++) {
2149 if (templ2->templCalledTab[l] == templ1) {
2150 snprintf(times_str,sizeof(times_str),"%8.3f",(float)templ2->time/XSLT_TIMESTAMP_TICS_PER_SEC);
2151 snprintf(timec_str,sizeof(timec_str),"%8.3f",(float)childt[k]/XSLT_TIMESTAMP_TICS_PER_SEC);
2152 snprintf(called_str,sizeof(called_str),"%6d/%d",
2153 templ2->templCountTab[l], /* number of times 'this' calls callee */
2154 total); /* total number of calls from 'this' */
2155 fprintf(output, " %-8s %-8s %-12s %s [%d]\n",
2156 times_str,timec_str,called_str,
2157 templ2->name?(char *)templ2->name:pretty_templ_match(templ2),k);
2158 }
2159 }
2160 }
2161 fprintf(output, "-----------------------------------------------\n");
2162 }
2163
2164 fprintf(output, "\f\nIndex by function name\n");
2165 for (i = 0; i < nb; i++) {
2166 templ1 = templates[i];
2167 fprintf(output, "[%d] %s (%s:%d)\n",
2168 i, templ1->name?(char *)templ1->name:pretty_templ_match(templ1),
2169 templ1->style->doc->URL,templ1->elem->line);
2170 }
2171
2172 fprintf(output, "\f\n");
2173 xmlFree(childt);
2174
2175 xmlFree(templates);
2176 }
2177
2178 /************************************************************************
2179 * *
2180 * Fetching profiling information *
2181 * *
2182 ************************************************************************/
2183
2184 /**
2185 * xsltGetProfileInformation:
2186 * @ctxt: a transformation context
2187 *
2188 * This function should be called after the transformation completed
2189 * to extract template processing profiling information if available.
2190 * The information is returned as an XML document tree like
2191 * <?xml version="1.0"?>
2192 * <profile>
2193 * <template rank="1" match="*" name=""
2194 * mode="" calls="6" time="48" average="8"/>
2195 * <template rank="2" match="item2|item3" name=""
2196 * mode="" calls="10" time="30" average="3"/>
2197 * <template rank="3" match="item1" name=""
2198 * mode="" calls="5" time="17" average="3"/>
2199 * </profile>
2200 * The caller will need to free up the returned tree with xmlFreeDoc()
2201 *
2202 * Returns the xmlDocPtr corresponding to the result or NULL if not available.
2203 */
2204
2205 xmlDocPtr
xsltGetProfileInformation(xsltTransformContextPtr ctxt)2206 xsltGetProfileInformation(xsltTransformContextPtr ctxt)
2207 {
2208 xmlDocPtr ret = NULL;
2209 xmlNodePtr root, child;
2210 char buf[100];
2211
2212 xsltStylesheetPtr style;
2213 xsltTemplatePtr *templates;
2214 xsltTemplatePtr templ;
2215 int nb = 0, max = 0, i, j;
2216
2217 if (!ctxt)
2218 return NULL;
2219
2220 if (!ctxt->profile)
2221 return NULL;
2222
2223 nb = 0;
2224 max = 10000;
2225 templates =
2226 (xsltTemplatePtr *) xmlMalloc(max * sizeof(xsltTemplatePtr));
2227 if (templates == NULL)
2228 return NULL;
2229
2230 /*
2231 * collect all the templates in an array
2232 */
2233 style = ctxt->style;
2234 while (style != NULL) {
2235 templ = style->templates;
2236 while (templ != NULL) {
2237 if (nb >= max)
2238 break;
2239
2240 if (templ->nbCalls > 0)
2241 templates[nb++] = templ;
2242 templ = templ->next;
2243 }
2244
2245 style = (xsltStylesheetPtr) xsltNextImport(style);
2246 }
2247
2248 /*
2249 * Sort the array by time spent
2250 */
2251 for (i = 0; i < nb - 1; i++) {
2252 for (j = i + 1; j < nb; j++) {
2253 if ((templates[i]->time <= templates[j]->time) ||
2254 ((templates[i]->time == templates[j]->time) &&
2255 (templates[i]->nbCalls <= templates[j]->nbCalls))) {
2256 templ = templates[j];
2257 templates[j] = templates[i];
2258 templates[i] = templ;
2259 }
2260 }
2261 }
2262
2263 /*
2264 * Generate a document corresponding to the results.
2265 */
2266 ret = xmlNewDoc(BAD_CAST "1.0");
2267 root = xmlNewDocNode(ret, NULL, BAD_CAST "profile", NULL);
2268 xmlDocSetRootElement(ret, root);
2269
2270 for (i = 0; i < nb; i++) {
2271 child = xmlNewChild(root, NULL, BAD_CAST "template", NULL);
2272 snprintf(buf, sizeof(buf), "%d", i + 1);
2273 xmlSetProp(child, BAD_CAST "rank", BAD_CAST buf);
2274 xmlSetProp(child, BAD_CAST "match", BAD_CAST templates[i]->match);
2275 xmlSetProp(child, BAD_CAST "name", BAD_CAST templates[i]->name);
2276 xmlSetProp(child, BAD_CAST "mode", BAD_CAST templates[i]->mode);
2277
2278 snprintf(buf, sizeof(buf), "%d", templates[i]->nbCalls);
2279 xmlSetProp(child, BAD_CAST "calls", BAD_CAST buf);
2280
2281 snprintf(buf, sizeof(buf), "%ld", templates[i]->time);
2282 xmlSetProp(child, BAD_CAST "time", BAD_CAST buf);
2283
2284 snprintf(buf, sizeof(buf), "%ld", templates[i]->time / templates[i]->nbCalls);
2285 xmlSetProp(child, BAD_CAST "average", BAD_CAST buf);
2286 };
2287
2288 xmlFree(templates);
2289
2290 return ret;
2291 }
2292
2293 #endif /* WITH_PROFILER */
2294
2295 /************************************************************************
2296 * *
2297 * Hooks for libxml2 XPath *
2298 * *
2299 ************************************************************************/
2300
2301 /**
2302 * xsltXPathCompileFlags:
2303 * @style: the stylesheet
2304 * @str: the XPath expression
2305 * @flags: extra compilation flags to pass down to libxml2 XPath
2306 *
2307 * Compile an XPath expression
2308 *
2309 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2310 * the caller has to free the object.
2311 */
2312 xmlXPathCompExprPtr
xsltXPathCompileFlags(xsltStylesheetPtr style,const xmlChar * str,int flags)2313 xsltXPathCompileFlags(xsltStylesheetPtr style, const xmlChar *str, int flags) {
2314 xmlXPathContextPtr xpathCtxt;
2315 xmlXPathCompExprPtr ret;
2316
2317 if (style != NULL) {
2318 xpathCtxt = style->principal->xpathCtxt;
2319 if (xpathCtxt == NULL)
2320 return NULL;
2321 xpathCtxt->dict = style->dict;
2322 } else {
2323 xpathCtxt = xmlXPathNewContext(NULL);
2324 if (xpathCtxt == NULL)
2325 return NULL;
2326 }
2327 xpathCtxt->flags = flags;
2328
2329 /*
2330 * Compile the expression.
2331 */
2332 ret = xmlXPathCtxtCompile(xpathCtxt, str);
2333
2334 if (style == NULL) {
2335 xmlXPathFreeContext(xpathCtxt);
2336 }
2337 /*
2338 * TODO: there is a lot of optimizations which should be possible
2339 * like variable slot precomputations, function precomputations, etc.
2340 */
2341
2342 return(ret);
2343 }
2344
2345 /**
2346 * xsltXPathCompile:
2347 * @style: the stylesheet
2348 * @str: the XPath expression
2349 *
2350 * Compile an XPath expression
2351 *
2352 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
2353 * the caller has to free the object.
2354 */
2355 xmlXPathCompExprPtr
xsltXPathCompile(xsltStylesheetPtr style,const xmlChar * str)2356 xsltXPathCompile(xsltStylesheetPtr style, const xmlChar *str) {
2357 return(xsltXPathCompileFlags(style, str, 0));
2358 }
2359
2360 /************************************************************************
2361 * *
2362 * Hooks for the debugger *
2363 * *
2364 ************************************************************************/
2365
2366 int xslDebugStatus;
2367
2368 /**
2369 * xsltGetDebuggerStatus:
2370 *
2371 * Get xslDebugStatus.
2372 *
2373 * Returns the value of xslDebugStatus.
2374 */
2375 int
xsltGetDebuggerStatus(void)2376 xsltGetDebuggerStatus(void)
2377 {
2378 return(xslDebugStatus);
2379 }
2380
2381 #ifdef WITH_DEBUGGER
2382
2383 /*
2384 * There is currently only 3 debugging callback defined
2385 * Debugger callbacks are disabled by default
2386 */
2387 #define XSLT_CALLBACK_NUMBER 3
2388
2389 typedef struct _xsltDebuggerCallbacks xsltDebuggerCallbacks;
2390 typedef xsltDebuggerCallbacks *xsltDebuggerCallbacksPtr;
2391 struct _xsltDebuggerCallbacks {
2392 xsltHandleDebuggerCallback handler;
2393 xsltAddCallCallback add;
2394 xsltDropCallCallback drop;
2395 };
2396
2397 static xsltDebuggerCallbacks xsltDebuggerCurrentCallbacks = {
2398 NULL, /* handler */
2399 NULL, /* add */
2400 NULL /* drop */
2401 };
2402
2403 /**
2404 * xsltSetDebuggerStatus:
2405 * @value : the value to be set
2406 *
2407 * This function sets the value of xslDebugStatus.
2408 */
2409 void
xsltSetDebuggerStatus(int value)2410 xsltSetDebuggerStatus(int value)
2411 {
2412 xslDebugStatus = value;
2413 }
2414
2415 /**
2416 * xsltSetDebuggerCallbacks:
2417 * @no : number of callbacks
2418 * @block : the block of callbacks
2419 *
2420 * This function allow to plug a debugger into the XSLT library
2421 * @block points to a block of memory containing the address of @no
2422 * callback routines.
2423 *
2424 * Returns 0 in case of success and -1 in case of error
2425 */
2426 int
xsltSetDebuggerCallbacks(int no,void * block)2427 xsltSetDebuggerCallbacks(int no, void *block)
2428 {
2429 xsltDebuggerCallbacksPtr callbacks;
2430
2431 if ((block == NULL) || (no != XSLT_CALLBACK_NUMBER))
2432 return(-1);
2433
2434 callbacks = (xsltDebuggerCallbacksPtr) block;
2435 xsltDebuggerCurrentCallbacks.handler = callbacks->handler;
2436 xsltDebuggerCurrentCallbacks.add = callbacks->add;
2437 xsltDebuggerCurrentCallbacks.drop = callbacks->drop;
2438 return(0);
2439 }
2440
2441 /**
2442 * xslHandleDebugger:
2443 * @cur : source node being executed
2444 * @node : data node being processed
2445 * @templ : temlate that applies to node
2446 * @ctxt : the xslt transform context
2447 *
2448 * If either cur or node are a breakpoint, or xslDebugStatus in state
2449 * where debugging must occcur at this time then transfer control
2450 * to the xslDebugBreak function
2451 */
2452 void
xslHandleDebugger(xmlNodePtr cur,xmlNodePtr node,xsltTemplatePtr templ,xsltTransformContextPtr ctxt)2453 xslHandleDebugger(xmlNodePtr cur, xmlNodePtr node, xsltTemplatePtr templ,
2454 xsltTransformContextPtr ctxt)
2455 {
2456 if (xsltDebuggerCurrentCallbacks.handler != NULL)
2457 xsltDebuggerCurrentCallbacks.handler(cur, node, templ, ctxt);
2458 }
2459
2460 /**
2461 * xslAddCall:
2462 * @templ : current template being applied
2463 * @source : the source node being processed
2464 *
2465 * Add template "call" to call stack
2466 * Returns : 1 on sucess 0 otherwise an error may be printed if
2467 * WITH_XSLT_DEBUG_BREAKPOINTS is defined
2468 */
2469 int
xslAddCall(xsltTemplatePtr templ,xmlNodePtr source)2470 xslAddCall(xsltTemplatePtr templ, xmlNodePtr source)
2471 {
2472 if (xsltDebuggerCurrentCallbacks.add != NULL)
2473 return(xsltDebuggerCurrentCallbacks.add(templ, source));
2474 return(0);
2475 }
2476
2477 /**
2478 * xslDropCall:
2479 *
2480 * Drop the topmost item off the call stack
2481 */
2482 void
xslDropCall(void)2483 xslDropCall(void)
2484 {
2485 if (xsltDebuggerCurrentCallbacks.drop != NULL)
2486 xsltDebuggerCurrentCallbacks.drop();
2487 }
2488
2489 #endif /* WITH_DEBUGGER */
2490
2491