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