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