- xsltutils.[ch] transform.c: implemented multiple levels of
[platform/upstream/libxslt.git] / libxslt / xsltutils.c
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@imag.fr
10  */
11
12 #include "xsltconfig.h"
13
14 #include <stdio.h>
15 #include <stdarg.h>
16
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"
26 #include "imports.h"
27
28 /************************************************************************
29  *                                                                      *
30  *              Handling of XSLT stylesheets messages                   *
31  *                                                                      *
32  ************************************************************************/
33
34 /**
35  * xsltMessage:
36  * @ctxt:  an XSLT processing context
37  * @node:  The current node
38  * @inst:  The node containing the message instruction
39  *
40  * Process and xsl:message construct
41  */
42 void
43 xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
44     xmlChar *prop, *message;
45     int terminate = 0;
46
47     if ((ctxt == NULL) || (inst == NULL))
48         return;
49
50     prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", XSLT_NAMESPACE);
51     if (prop != NULL) {
52         if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
53             terminate = 1;
54         } else if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
55             terminate = 0;
56         } else {
57             xsltGenericError(xsltGenericErrorContext,
58                 "xsl:message : terminate expecting 'yes' or 'no'\n");
59         }
60         xmlFree(prop);
61     }
62     message = xsltEvalTemplateString(ctxt, node, inst);
63     if (message != NULL) {
64         int len = xmlStrlen(message);
65
66         xsltGenericError(xsltGenericErrorContext, (const char *)message);
67         if ((len > 0) && (message[len - 1] != '\n'))
68             xsltGenericError(xsltGenericErrorContext, "\n");
69         xmlFree(message);
70     }
71     if (terminate)
72         ctxt->state = XSLT_STATE_STOPPED;
73 }
74
75 /************************************************************************
76  *                                                                      *
77  *              Handling of out of context errors                       *
78  *                                                                      *
79  ************************************************************************/
80
81 /**
82  * xsltGenericErrorDefaultFunc:
83  * @ctx:  an error context
84  * @msg:  the message to display/transmit
85  * @...:  extra parameters for the message display
86  * 
87  * Default handler for out of context error messages.
88  */
89 static void
90 xsltGenericErrorDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
91     va_list args;
92
93     if (xsltGenericErrorContext == NULL)
94         xsltGenericErrorContext = (void *) stderr;
95
96     va_start(args, msg);
97     vfprintf((FILE *)xsltGenericErrorContext, msg, args);
98     va_end(args);
99 }
100
101 xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
102 void *xsltGenericErrorContext = NULL;
103
104
105 /**
106  * xsltSetGenericErrorFunc:
107  * @ctx:  the new error handling context
108  * @handler:  the new handler function
109  *
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.
117  */
118 void
119 xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
120     xsltGenericErrorContext = ctx;
121     if (handler != NULL)
122         xsltGenericError = handler;
123     else
124         xsltGenericError = xsltGenericErrorDefaultFunc;
125 }
126
127 /**
128  * xsltGenericDebugDefaultFunc:
129  * @ctx:  an error context
130  * @msg:  the message to display/transmit
131  * @...:  extra parameters for the message display
132  * 
133  * Default handler for out of context error messages.
134  */
135 static void
136 xsltGenericDebugDefaultFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
137     va_list args;
138
139     if (xsltGenericDebugContext == NULL)
140         return;
141
142     va_start(args, msg);
143     vfprintf((FILE *)xsltGenericDebugContext, msg, args);
144     va_end(args);
145 }
146
147 xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
148 void *xsltGenericDebugContext = NULL;
149
150
151 /**
152  * xsltSetGenericDebugFunc:
153  * @ctx:  the new error handling context
154  * @handler:  the new handler function
155  *
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.
163  */
164 void
165 xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
166     xsltGenericDebugContext = ctx;
167     if (handler != NULL)
168         xsltGenericDebug = handler;
169     else
170         xsltGenericDebug = xsltGenericDebugDefaultFunc;
171 }
172
173 /************************************************************************
174  *                                                                      *
175  *                              Sorting                                 *
176  *                                                                      *
177  ************************************************************************/
178
179 /**
180  * xsltDocumentSortFunction:
181  * @list:  the node set
182  *
183  * reorder the current node list @list accordingly to the document order
184  */
185 void
186 xsltDocumentSortFunction(xmlNodeSetPtr list) {
187     int i, j;
188     int len, tst;
189     xmlNodePtr node;
190
191     if (list == NULL)
192         return;
193     len = list->nodeNr;
194     if (len <= 1)
195         return;
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]);
200             if (tst == -1) {
201                 node = list->nodeTab[i];
202                 list->nodeTab[i] = list->nodeTab[j];
203                 list->nodeTab[j] = node;
204             }
205         }
206     }
207 }
208
209 /**
210  * xsltComputeSortResult:
211  * @ctxt:  a XSLT process context
212  * @sorts:  array of sort nodes
213  * @nbsorts:  the number of sorts in the array
214  *
215  * reorder the current node list accordingly to the set of sorting
216  * requirement provided by the arry of nodes.
217  */
218 static xmlXPathObjectPtr *
219 xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) {
220     xmlXPathObjectPtr *results = NULL;
221     xmlNodeSetPtr list = NULL;
222     xmlXPathObjectPtr res;
223     int len = 0;
224     int i;
225     xmlNodePtr oldNode;
226     xsltStylePreCompPtr comp;
227
228     comp = sort->_private;
229     if (comp == NULL) {
230         xsltGenericError(xsltGenericErrorContext,
231              "xslt:sort : compilation had failed\n");
232         return(NULL);
233     }
234
235     if (comp->select == NULL)
236         return(NULL);
237     if (comp->comp == NULL) {
238         comp->comp = xmlXPathCompile(comp->select);
239         if (comp->comp == NULL)
240             return(NULL);
241     }
242
243
244     list = ctxt->nodeList;
245     if ((list == NULL) || (list->nodeNr <= 1))
246         return(NULL);
247
248     len = list->nodeNr;
249
250     /* TODO: xsl:sort lang attribute */
251     /* TODO: xsl:sort case-order attribute */
252
253
254     results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
255     if (results == NULL) {
256         xsltGenericError(xsltGenericErrorContext,
257              "xsltSort: memory allocation failure\n");
258         return(NULL);
259     }
260
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);
270         if (res != NULL) {
271             if (res->type != XPATH_STRING)
272                 res = xmlXPathConvertString(res);
273             if (comp->number)
274                 res = xmlXPathConvertNumber(res);
275             res->index = i;     /* Save original pos for dupl resolv */
276             if (comp->number) {
277                 if (res->type == XPATH_NUMBER) {
278                     results[i] = res;
279                 } else {
280 #ifdef WITH_XSLT_DEBUG_PROCESS
281                     xsltGenericDebug(xsltGenericDebugContext,
282                         "xsltSort: select didn't evaluate to a number\n");
283 #endif
284                     results[i] = NULL;
285                 }
286             } else {
287                 if (res->type == XPATH_STRING) {
288                     results[i] = res;
289                 } else {
290 #ifdef WITH_XSLT_DEBUG_PROCESS
291                     xsltGenericDebug(xsltGenericDebugContext,
292                         "xsltSort: select didn't evaluate to a string\n");
293 #endif
294                     results[i] = NULL;
295                 }
296             }
297         }
298     }
299     ctxt->node = oldNode;
300
301     return(results);
302 }
303
304 /**
305  * xsltDoSortFunction:
306  * @ctxt:  a XSLT process context
307  * @sorts:  array of sort nodes
308  * @nbsorts:  the number of sorts in the array
309  *
310  * reorder the current node list accordingly to the set of sorting
311  * requirement provided by the arry of nodes.
312  */
313 void    
314 xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
315                    int nbsorts) {
316     xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT];
317     xmlXPathObjectPtr *results = NULL, *res;
318     xmlNodeSetPtr list = NULL;
319     int descending, number, desc, numb;
320     int len = 0;
321     int i, j, incr;
322     int tst;
323     int depth;
324     xmlNodePtr node;
325     xmlXPathObjectPtr tmp;
326     xsltStylePreCompPtr comp;
327
328     if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) ||
329         (nbsorts >= XSLT_MAX_SORT))
330         return;
331     if (sorts[0] == NULL)
332         return;
333     comp = sorts[0]->_private;
334     if (comp == NULL)
335         return;
336
337     list = ctxt->nodeList;
338     if ((list == NULL) || (list->nodeNr <= 1))
339         return; /* nothing to do */
340
341     len = list->nodeNr;
342
343     resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]);
344
345     results = resultsTab[0];
346
347     descending = comp->descending;
348     number = comp->number;
349     if (results == NULL)
350         return;
351
352     /* Shell's sort of node-set */
353     for (incr = len / 2; incr > 0; incr /= 2) {
354         for (i = incr; i < len; i++) {
355             j = i - incr;
356             if (results[i] == NULL)
357                 continue;
358             
359             while (j >= 0) {
360                 if (results[j] == NULL)
361                     tst = 1;
362                 else {
363                     if (number) {
364                         if (results[j]->floatval == results[j + incr]->floatval)
365                             tst = 0;
366                         else if (results[j]->floatval > 
367                                 results[j + incr]->floatval)
368                             tst = 1;
369                         else tst = -1;
370                     } else {
371                         tst = xmlStrcmp(results[j]->stringval,
372                                      results[j + incr]->stringval); 
373                     }
374                     if (tst == 0)
375                         tst = results[j]->index > results[j + incr]->index;
376                     if (descending)
377                         tst = -tst;
378                 }
379                 if (tst == 0) {
380                     /*
381                      * Okay we need to use multi level sorts
382                      */
383                     depth = 1;
384                     while (depth < nbsorts) {
385                         if (sorts[depth] == NULL)
386                             break;
387                         comp = sorts[depth]->_private;
388                         if (comp == NULL)
389                             break;
390                         desc = comp->descending;
391                         numb = comp->number;
392
393                         /*
394                          * Compute the result of the next level for the
395                          * full set, this might be optimized ... or not
396                          */
397                         if (resultsTab[depth] == NULL) 
398                             resultsTab[depth] = xsltComputeSortResult(ctxt,
399                                                         sorts[depth]);
400                         res = resultsTab[depth];
401                         if (res == NULL) 
402                             break;
403                         if (res[j] == NULL)
404                             tst = 1;
405                         else {
406                             if (numb) {
407                                 if (res[j]->floatval == res[j + incr]->floatval)
408                                     tst = 0;
409                                 else if (res[j]->floatval > 
410                                         res[j + incr]->floatval)
411                                     tst = 1;
412                                 else tst = -1;
413                             } else {
414                                 tst = xmlStrcmp(res[j]->stringval,
415                                              res[j + incr]->stringval); 
416                             }
417                             if (tst == 0)
418                                 tst = res[j]->index > res[j + incr]->index;
419                             if (desc)
420                                 tst = -tst;
421                         }
422
423                         /*
424                          * if we still can't differenciate at this level
425                          * try one level deeper.
426                          */
427                         if (tst != 0)
428                             break;
429                         depth++;
430                     }
431                 }
432                 if (tst > 0) {
433                     tmp = results[j];
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;
439                     j -= incr;
440                 } else
441                     break;
442             }
443         }
444     }
445
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]);
451         }
452     }
453 }
454
455 /************************************************************************
456  *                                                                      *
457  *                              Output                                  *
458  *                                                                      *
459  ************************************************************************/
460
461 /**
462  * xsltSaveResultTo:
463  * @buf:  an output buffer
464  * @result:  the result xmlDocPtr
465  * @style:  the stylesheet
466  *
467  * Save the result @result obtained by applying the @style stylesheet
468  * to an I/O output channel @buf
469  *
470  * Returns the number of byte written or -1 in case of failure.
471  */
472 int
473 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
474                xsltStylesheetPtr style) {
475     const xmlChar *encoding;
476     xmlNodePtr root;
477     int base;
478     const xmlChar *method;
479
480     if ((buf == NULL) || (result == NULL) || (style == NULL))
481         return(-1);
482
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");
488         return(-1);
489     }
490
491     /* TODO: when outputing and having imported stylesheets, apply cascade */
492     base = buf->written;
493
494     XSLT_GET_IMPORT_PTR(method, style, method)
495     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
496
497     if (method == NULL)
498         root = xmlDocGetRootElement(result);
499     else
500         root = NULL;
501
502     if ((method != NULL) &&
503         (xmlStrEqual(method, (const xmlChar *) "html"))) {
504         if (encoding != NULL) {
505             htmlSetMetaEncoding(result, (const xmlChar *) encoding);
506         } else {
507             htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
508         }
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);
514         } else {
515             htmlSetMetaEncoding(result, (const xmlChar *) "UTF-8");
516         }
517         htmlDocContentDumpOutput(buf, result, (const char *) encoding);
518     } else if ((method != NULL) &&
519                (xmlStrEqual(method, (const xmlChar *) "text"))) {
520         xmlNodePtr cur;
521
522         cur = result->children;
523         while (cur != NULL) {
524             if (cur->type == XML_TEXT_NODE)
525                 xmlOutputBufferWriteString(buf, (const char *) cur->content);
526             cur = cur->next;
527         }
528     } else {
529         int omitXmlDecl;
530         int standalone;
531         int indent;
532         const xmlChar *version;
533         const xmlChar *doctypePublic;
534         const xmlChar *doctypeSystem;
535
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)
542
543         if (omitXmlDecl != 1) {
544             xmlOutputBufferWriteString(buf, "<?xml version=");
545             if (result->version != NULL) 
546                 xmlBufferWriteQuotedString(buf->buffer, result->version);
547             else
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)
555                                                       result->charset);
556             }
557             if (encoding != NULL) {
558                 xmlOutputBufferWriteString(buf, " encoding=");
559                 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
560             }
561             switch (standalone) {
562                 case 0:
563                     xmlOutputBufferWriteString(buf, " standalone=\"no\"");
564                     break;
565                 case 1:
566                     xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
567                     break;
568                 default:
569                     break;
570             }
571             xmlOutputBufferWriteString(buf, "?>\n");
572         }
573         if ((doctypePublic != NULL) || (doctypeSystem != NULL)) {
574             xmlNodePtr cur = result->children;
575
576             while (cur != NULL) {
577                 if (cur->type == XML_ELEMENT_NODE)
578                     break;
579                 cur = cur->next;
580             }
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,
588                                          doctypePublic);
589                         xmlOutputBufferWriteString(buf, " ");
590                         xmlBufferWriteQuotedString(buf->buffer,
591                                          doctypeSystem);
592                     } else {
593                         xmlOutputBufferWriteString(buf, " PUBLIC \"-\" ");
594                         xmlBufferWriteQuotedString(buf->buffer,
595                                          doctypeSystem);
596                     }
597
598                 } else {
599                     xmlOutputBufferWriteString(buf, " SYSTEM ");
600                     xmlBufferWriteQuotedString(buf->buffer,
601                                      doctypeSystem);
602                 }
603                 xmlOutputBufferWriteString(buf, ">\n");
604             }
605         }
606         if (result->children != NULL) {
607             xmlNodePtr child = result->children;
608
609             while (child != NULL) {
610                 xmlNodeDumpOutput(buf, result, child, 0, (indent == 1),
611                                   (const char *) encoding);
612                 xmlOutputBufferWriteString(buf, "\n");
613                 child = child->next;
614             }
615         }
616         xmlOutputBufferFlush(buf);
617     }
618     return(buf->written - base);
619 }
620
621 /**
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)
627  *
628  * Save the result @result obtained by applying the @style stylesheet
629  * to a file or URL @URL
630  *
631  * Returns the number of byte written or -1 in case of failure.
632  */
633 int
634 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
635                          xsltStylesheetPtr style, int compression) {
636     xmlOutputBufferPtr buf;
637     const xmlChar *encoding;
638     int ret;
639
640     if ((URL == NULL) || (result == NULL) || (style == NULL))
641         return(-1);
642
643     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
644     if (encoding != NULL) {
645         xmlCharEncodingHandlerPtr encoder;
646
647         encoder = xmlFindCharEncodingHandler((char *)encoding);
648         if ((encoder != NULL) &&
649             (xmlStrEqual((const xmlChar *)encoder->name,
650                          (const xmlChar *) "UTF-8")))
651             encoder = NULL;
652         buf = xmlOutputBufferCreateFilename(URL, encoder, compression);
653     } else {
654         buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
655     }
656     if (buf == NULL)
657         return(-1);
658     xsltSaveResultTo(buf, result, style);
659     ret = xmlOutputBufferClose(buf);
660     return(ret);
661 }
662
663 /**
664  * xsltSaveResultToFile:
665  * @file:  a FILE * I/O
666  * @result:  the result xmlDocPtr
667  * @style:  the stylesheet
668  *
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
672  *
673  * Returns the number of byte written or -1 in case of failure.
674  */
675 int
676 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
677     xmlOutputBufferPtr buf;
678     const xmlChar *encoding;
679     int ret;
680
681     if ((file == NULL) || (result == NULL) || (style == NULL))
682         return(-1);
683
684     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
685     if (encoding != NULL) {
686         xmlCharEncodingHandlerPtr encoder;
687
688         encoder = xmlFindCharEncodingHandler((char *)encoding);
689         if ((encoder != NULL) &&
690             (xmlStrEqual((const xmlChar *)encoder->name,
691                          (const xmlChar *) "UTF-8")))
692             encoder = NULL;
693         buf = xmlOutputBufferCreateFile(file, encoder);
694     } else {
695         buf = xmlOutputBufferCreateFile(file, NULL);
696     }
697
698     if (buf == NULL)
699         return(-1);
700     xsltSaveResultTo(buf, result, style);
701     ret = xmlOutputBufferClose(buf);
702     return(ret);
703 }
704
705 /**
706  * xsltSaveResultToFd:
707  * @fd:  a file descriptor
708  * @result:  the result xmlDocPtr
709  * @style:  the stylesheet
710  *
711  * Save the result @result obtained by applying the @style stylesheet
712  * to an open file descriptor
713  * This does not close the descriptor.
714  *
715  * Returns the number of byte written or -1 in case of failure.
716  */
717 int
718 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
719     xmlOutputBufferPtr buf;
720     const xmlChar *encoding;
721     int ret;
722
723     if ((fd < 0) || (result == NULL) || (style == NULL))
724         return(-1);
725
726     XSLT_GET_IMPORT_PTR(encoding, style, encoding)
727     if (encoding != NULL) {
728         xmlCharEncodingHandlerPtr encoder;
729
730         encoder = xmlFindCharEncodingHandler((char *)encoding);
731         if ((encoder != NULL) &&
732             (xmlStrEqual((const xmlChar *)encoder->name,
733                          (const xmlChar *) "UTF-8")))
734             encoder = NULL;
735         buf = xmlOutputBufferCreateFd(fd, encoder);
736     } else {
737         buf = xmlOutputBufferCreateFd(fd, NULL);
738     }
739     if (buf == NULL)
740         return(-1);
741     xsltSaveResultTo(buf, result, style);
742     ret = xmlOutputBufferClose(buf);
743     return(ret);
744 }
745