2 * xsltutils.c: Utilities for the XSL Transformation 1.0 engine
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * Daniel.Veillard@imag.fr
12 #include "xsltconfig.h"
17 #include <libxml/xmlversion.h>
18 #include <libxml/xmlmemory.h>
19 #include <libxml/tree.h>
20 #include <libxml/HTMLtree.h>
21 #include <libxml/xmlerror.h>
22 #include <libxml/xmlIO.h>
23 #include "xsltutils.h"
24 #include "templates.h"
25 #include "xsltInternals.h"
28 /************************************************************************
30 * Handling of XSLT stylesheets messages *
32 ************************************************************************/
36 * @ctxt: an XSLT processing context
37 * @node: The current node
38 * @inst: The node containing the message instruction
40 * Process and xsl:message construct
43 xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
44 xmlChar *prop, *message;
47 if ((ctxt == NULL) || (inst == NULL))
50 prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", XSLT_NAMESPACE);
52 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
54 } else if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
57 xsltGenericError(xsltGenericErrorContext,
58 "xsl:message : terminate expecting 'yes' or 'no'\n");
62 message = xsltEvalTemplateString(ctxt, node, inst);
63 if (message != NULL) {
64 int len = xmlStrlen(message);
66 xsltGenericError(xsltGenericErrorContext, (const char *)message);
67 if ((len > 0) && (message[len - 1] != '\n'))
68 xsltGenericError(xsltGenericErrorContext, "\n");
72 ctxt->state = XSLT_STATE_STOPPED;
75 /************************************************************************
77 * Handling of out of context errors *
79 ************************************************************************/
82 * xsltGenericErrorDefaultFunc:
83 * @ctx: an error context
84 * @msg: the message to display/transmit
85 * @...: extra parameters for the message display
87 * Default handler for out of context error messages.
90 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
93 if (xsltGenericErrorContext == NULL)
94 xsltGenericErrorContext = (void *) stderr;
97 vfprintf((FILE *)xsltGenericErrorContext, msg, args);
101 xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
102 void *xsltGenericErrorContext = NULL;
106 * xsltSetGenericErrorFunc:
107 * @ctx: the new error handling context
108 * @handler: the new handler function
110 * Function to reset the handler and the error context for out of
111 * context error messages.
112 * This simply means that @handler will be called for subsequent
113 * error messages while not parsing nor validating. And @ctx will
114 * be passed as first argument to @handler
115 * One can simply force messages to be emitted to another FILE * than
116 * stderr by setting @ctx to this file handle and @handler to NULL.
119 xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
120 xsltGenericErrorContext = ctx;
122 xsltGenericError = handler;
124 xsltGenericError = xsltGenericErrorDefaultFunc;
128 * xsltGenericDebugDefaultFunc:
129 * @ctx: an error context
130 * @msg: the message to display/transmit
131 * @...: extra parameters for the message display
133 * Default handler for out of context error messages.
136 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
139 if (xsltGenericDebugContext == NULL)
143 vfprintf((FILE *)xsltGenericDebugContext, msg, args);
147 xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
148 void *xsltGenericDebugContext = NULL;
152 * xsltSetGenericDebugFunc:
153 * @ctx: the new error handling context
154 * @handler: the new handler function
156 * Function to reset the handler and the error context for out of
157 * context error messages.
158 * This simply means that @handler will be called for subsequent
159 * error messages while not parsing nor validating. And @ctx will
160 * be passed as first argument to @handler
161 * One can simply force messages to be emitted to another FILE * than
162 * stderr by setting @ctx to this file handle and @handler to NULL.
165 xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
166 xsltGenericDebugContext = ctx;
168 xsltGenericDebug = handler;
170 xsltGenericDebug = xsltGenericDebugDefaultFunc;
173 /************************************************************************
177 ************************************************************************/
180 * xsltDocumentSortFunction:
181 * @list: the node set
183 * reorder the current node list @list accordingly to the document order
186 xsltDocumentSortFunction(xmlNodeSetPtr list) {
196 /* TODO: sort is really not optimized, does it needs to ? */
197 for (i = 0;i < len -1;i++) {
198 for (j = i + 1; j < len; j++) {
199 tst = xmlXPathCmpNodes(list->nodeTab[i], list->nodeTab[j]);
201 node = list->nodeTab[i];
202 list->nodeTab[i] = list->nodeTab[j];
203 list->nodeTab[j] = node;
210 * xsltComputeSortResult:
211 * @ctxt: a XSLT process context
212 * @sorts: array of sort nodes
213 * @nbsorts: the number of sorts in the array
215 * reorder the current node list accordingly to the set of sorting
216 * requirement provided by the arry of nodes.
218 static xmlXPathObjectPtr *
219 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
220 xmlXPathObjectPtr *results = NULL;
221 xmlNodeSetPtr list = NULL;
222 xmlXPathObjectPtr res;
226 xsltStylePreCompPtr comp;
228 comp = sort->_private;
230 xsltGenericError(xsltGenericErrorContext,
231 "xslt:sort : compilation had failed\n");
235 if (comp->select == NULL)
237 if (comp->comp == NULL) {
238 comp->comp = xmlXPathCompile(comp->select);
239 if (comp->comp == NULL)
244 list = ctxt->nodeList;
245 if ((list == NULL) || (list->nodeNr <= 1))
250 /* TODO: xsl:sort lang attribute */
251 /* TODO: xsl:sort case-order attribute */
254 results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
255 if (results == NULL) {
256 xsltGenericError(xsltGenericErrorContext,
257 "xsltSort: memory allocation failure\n");
261 oldNode = ctxt->node;
262 for (i = 0;i < len;i++) {
263 ctxt->xpathCtxt->contextSize = len;
264 ctxt->xpathCtxt->proximityPosition = i + 1;
265 ctxt->node = list->nodeTab[i];
266 ctxt->xpathCtxt->node = ctxt->node;
267 ctxt->xpathCtxt->namespaces = comp->nsList;
268 ctxt->xpathCtxt->nsNr = comp->nsNr;
269 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
271 if (res->type != XPATH_STRING)
272 res = xmlXPathConvertString(res);
274 res = xmlXPathConvertNumber(res);
275 res->index = i; /* Save original pos for dupl resolv */
277 if (res->type == XPATH_NUMBER) {
280 #ifdef WITH_XSLT_DEBUG_PROCESS
281 xsltGenericDebug(xsltGenericDebugContext,
282 "xsltSort: select didn't evaluate to a number\n");
287 if (res->type == XPATH_STRING) {
290 #ifdef WITH_XSLT_DEBUG_PROCESS
291 xsltGenericDebug(xsltGenericDebugContext,
292 "xsltSort: select didn't evaluate to a string\n");
299 ctxt->node = oldNode;
305 * xsltDoSortFunction:
306 * @ctxt: a XSLT process context
307 * @sorts: array of sort nodes
308 * @nbsorts: the number of sorts in the array
310 * reorder the current node list accordingly to the set of sorting
311 * requirement provided by the arry of nodes.
314 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
316 xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
317 xmlXPathObjectPtr *results = NULL, *res;
318 xmlNodeSetPtr list = NULL;
319 int descending, number, desc, numb;
325 xmlXPathObjectPtr tmp;
326 xsltStylePreCompPtr comp;
328 if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
329 (nbsorts >= XSLT_MAX_SORT))
331 if (sorts[0] == NULL)
333 comp = sorts[0]->_private;
337 list = ctxt->nodeList;
338 if ((list == NULL) || (list->nodeNr <= 1))
339 return; /* nothing to do */
343 resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
345 results = resultsTab[0];
347 descending = comp->descending;
348 number = comp->number;
352 /* Shell's sort of node-set */
353 for (incr = len / 2; incr > 0; incr /= 2) {
354 for (i = incr; i < len; i++) {
356 if (results[i] == NULL)
360 if (results[j] == NULL)
364 if (results[j]->floatval == results[j + incr]->floatval)
366 else if (results[j]->floatval >
367 results[j + incr]->floatval)
371 tst = xmlStrcmp(results[j]->stringval,
372 results[j + incr]->stringval);
375 tst = results[j]->index > results[j + incr]->index;
381 * Okay we need to use multi level sorts
384 while (depth < nbsorts) {
385 if (sorts[depth] == NULL)
387 comp = sorts[depth]->_private;
390 desc = comp->descending;
394 * Compute the result of the next level for the
395 * full set, this might be optimized ... or not
397 if (resultsTab[depth] == NULL)
398 resultsTab[depth] = xsltComputeSortResult(ctxt,
400 res = resultsTab[depth];
407 if (res[j]->floatval == res[j + incr]->floatval)
409 else if (res[j]->floatval >
410 res[j + incr]->floatval)
414 tst = xmlStrcmp(res[j]->stringval,
415 res[j + incr]->stringval);
418 tst = res[j]->index > res[j + incr]->index;
424 * if we still can't differenciate at this level
425 * try one level deeper.
434 results[j] = results[j + incr];
435 results[j + incr] = tmp;
436 node = list->nodeTab[j];
437 list->nodeTab[j] = list->nodeTab[j + incr];
438 list->nodeTab[j + incr] = node;
446 for (j = 0; j < nbsorts; j++) {
447 if (resultsTab[j] != NULL) {
448 for (i = 0;i < len;i++)
449 xmlXPathFreeObject(resultsTab[j][i]);
450 xmlFree(resultsTab[j]);
455 /************************************************************************
459 ************************************************************************/
463 * @buf: an output buffer
464 * @result: the result xmlDocPtr
465 * @style: the stylesheet
467 * Save the result @result obtained by applying the @style stylesheet
468 * to an I/O output channel @buf
470 * Returns the number of byte written or -1 in case of failure.
473 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
474 xsltStylesheetPtr style) {
475 const xmlChar *encoding;
478 const xmlChar *method;
480 if ((buf == NULL) || (result == NULL) || (style == NULL))
483 if ((style->methodURI != NULL) &&
484 ((style->method == NULL) ||
485 (!xmlStrEqual(style->method, (const xmlChar *) "xhtml")))) {
486 xsltGenericError(xsltGenericErrorContext,
487 "xsltSaveResultTo : unknown ouput method\n");
491 /* TODO: when outputing and having imported stylesheets, apply cascade */
494 XSLT_GET_IMPORT_PTR(method, style, method)
495 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
498 root = xmlDocGetRootElement(result);
502 if ((method != NULL) &&
503 (xmlStrEqual(method, (const xmlChar *) "html"))) {
504 if (encoding != NULL) {
505 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
507 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
509 htmlDocContentDumpOutput(buf, result, (const char *) encoding);
510 } else if ((method != NULL) &&
511 (xmlStrEqual(method, (const xmlChar *) "xhtml"))) {
512 if (encoding != NULL) {
513 htmlSetMetaEncoding(result, (const xmlChar *) encoding);
515 htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
517 htmlDocContentDumpOutput(buf, result, (const char *) encoding);
518 } else if ((method != NULL) &&
519 (xmlStrEqual(method, (const xmlChar *) "text"))) {
522 cur = result->children;
523 while (cur != NULL) {
524 if (cur->type == XML_TEXT_NODE)
525 xmlOutputBufferWriteString(buf, (const char *) cur->content);
532 const xmlChar *version;
533 const xmlChar *doctypePublic;
534 const xmlChar *doctypeSystem;
536 XSLT_GET_IMPORT_INT(omitXmlDecl, style, omitXmlDeclaration);
537 XSLT_GET_IMPORT_INT(standalone, style, standalone);
538 XSLT_GET_IMPORT_INT(indent, style, indent);
539 XSLT_GET_IMPORT_PTR(version, style, version)
540 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
541 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
543 if (omitXmlDecl != 1) {
544 xmlOutputBufferWriteString(buf, "<?xml version=");
545 if (result->version != NULL)
546 xmlBufferWriteQuotedString(buf->buffer, result->version);
548 xmlOutputBufferWriteString(buf, "\"1.0\"");
549 if (encoding == NULL) {
550 if (result->encoding != NULL)
551 encoding = result->encoding;
552 else if (result->charset != XML_CHAR_ENCODING_UTF8)
553 encoding = (const xmlChar *)
554 xmlGetCharEncodingName((xmlCharEncoding)
557 if (encoding != NULL) {
558 xmlOutputBufferWriteString(buf, " encoding=");
559 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
561 switch (standalone) {
563 xmlOutputBufferWriteString(buf, " standalone=\"no\"");
566 xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
571 xmlOutputBufferWriteString(buf, "?>\n");
573 if ((doctypePublic != NULL) || (doctypeSystem != NULL)) {
574 xmlNodePtr cur = result->children;
576 while (cur != NULL) {
577 if (cur->type == XML_ELEMENT_NODE)
581 if ((cur != NULL) && (cur->name != NULL)) {
582 xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
583 xmlOutputBufferWriteString(buf, (const char *) cur->name);
584 if (doctypePublic != NULL) {
585 if (doctypeSystem != NULL) {
586 xmlOutputBufferWriteString(buf, " PUBLIC ");
587 xmlBufferWriteQuotedString(buf->buffer,
589 xmlOutputBufferWriteString(buf, " ");
590 xmlBufferWriteQuotedString(buf->buffer,
593 xmlOutputBufferWriteString(buf, " PUBLIC \"-\" ");
594 xmlBufferWriteQuotedString(buf->buffer,
599 xmlOutputBufferWriteString(buf, " SYSTEM ");
600 xmlBufferWriteQuotedString(buf->buffer,
603 xmlOutputBufferWriteString(buf, ">\n");
606 if (result->children != NULL) {
607 xmlNodePtr child = result->children;
609 while (child != NULL) {
610 xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
611 (const char *) encoding);
612 xmlOutputBufferWriteString(buf, "\n");
616 xmlOutputBufferFlush(buf);
618 return(buf->written - base);
622 * xsltSaveResultToFilename:
623 * @URL: a filename or URL
624 * @result: the result xmlDocPtr
625 * @style: the stylesheet
626 * @compression: the compression factor (0 - 9 included)
628 * Save the result @result obtained by applying the @style stylesheet
629 * to a file or URL @URL
631 * Returns the number of byte written or -1 in case of failure.
634 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
635 xsltStylesheetPtr style, int compression) {
636 xmlOutputBufferPtr buf;
637 const xmlChar *encoding;
640 if ((URL == NULL) || (result == NULL) || (style == NULL))
643 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
644 if (encoding != NULL) {
645 xmlCharEncodingHandlerPtr encoder;
647 encoder = xmlFindCharEncodingHandler((char *)encoding);
648 if ((encoder != NULL) &&
649 (xmlStrEqual((const xmlChar *)encoder->name,
650 (const xmlChar *) "UTF-8")))
652 buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
654 buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
658 xsltSaveResultTo(buf, result, style);
659 ret = xmlOutputBufferClose(buf);
664 * xsltSaveResultToFile:
665 * @file: a FILE * I/O
666 * @result: the result xmlDocPtr
667 * @style: the stylesheet
669 * Save the result @result obtained by applying the @style stylesheet
670 * to an open FILE * I/O.
671 * This does not close the FILE @file
673 * Returns the number of byte written or -1 in case of failure.
676 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
677 xmlOutputBufferPtr buf;
678 const xmlChar *encoding;
681 if ((file == NULL) || (result == NULL) || (style == NULL))
684 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
685 if (encoding != NULL) {
686 xmlCharEncodingHandlerPtr encoder;
688 encoder = xmlFindCharEncodingHandler((char *)encoding);
689 if ((encoder != NULL) &&
690 (xmlStrEqual((const xmlChar *)encoder->name,
691 (const xmlChar *) "UTF-8")))
693 buf = xmlOutputBufferCreateFile(file, encoder);
695 buf = xmlOutputBufferCreateFile(file, NULL);
700 xsltSaveResultTo(buf, result, style);
701 ret = xmlOutputBufferClose(buf);
706 * xsltSaveResultToFd:
707 * @fd: a file descriptor
708 * @result: the result xmlDocPtr
709 * @style: the stylesheet
711 * Save the result @result obtained by applying the @style stylesheet
712 * to an open file descriptor
713 * This does not close the descriptor.
715 * Returns the number of byte written or -1 in case of failure.
718 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
719 xmlOutputBufferPtr buf;
720 const xmlChar *encoding;
723 if ((fd < 0) || (result == NULL) || (style == NULL))
726 XSLT_GET_IMPORT_PTR(encoding, style, encoding)
727 if (encoding != NULL) {
728 xmlCharEncodingHandlerPtr encoder;
730 encoder = xmlFindCharEncodingHandler((char *)encoding);
731 if ((encoder != NULL) &&
732 (xmlStrEqual((const xmlChar *)encoder->name,
733 (const xmlChar *) "UTF-8")))
735 buf = xmlOutputBufferCreateFd(fd, encoder);
737 buf = xmlOutputBufferCreateFd(fd, NULL);
741 xsltSaveResultTo(buf, result, style);
742 ret = xmlOutputBufferClose(buf);