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