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