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