71fdbba0d0261fcce073d4ebdaacde63e9b45c70
[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/xmlerror.h>
21 #include <libxml/xmlIO.h>
22 #include "xsltutils.h"
23 #include "templates.h"
24 #include "xsltInternals.h"
25
26
27 /************************************************************************
28  *                                                                      *
29  *              Handling of XSLT stylesheets messages                   *
30  *                                                                      *
31  ************************************************************************/
32
33 /**
34  * xsltMessage:
35  * @ctxt:  an XSLT processing context
36  * @node:  The current node
37  * @inst:  The node containing the message instruction
38  *
39  * Process and xsl:message construct
40  */
41 void
42 xsltMessage(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst) {
43     xmlChar *prop, *message;
44     int terminate = 0;
45
46     if ((ctxt == NULL) || (inst == NULL))
47         return;
48
49     prop = xmlGetNsProp(inst, (const xmlChar *)"terminate", XSLT_NAMESPACE);
50     if (prop != NULL) {
51         if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
52             terminate = 1;
53         } else if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
54             terminate = 0;
55         } else {
56             xsltGenericError(xsltGenericErrorContext,
57                 "xsl:message : terminate expecting 'yes' or 'no'\n");
58         }
59         xmlFree(prop);
60     }
61     message = xsltEvalTemplateString(ctxt, node, inst);
62     if (message != NULL) {
63         xsltGenericError(xsltGenericErrorContext, (const char *)message);
64         xmlFree(message);
65     }
66     if (terminate)
67         ctxt->state = XSLT_STATE_STOPPED;
68 }
69
70 /************************************************************************
71  *                                                                      *
72  *              Handling of out of context errors                       *
73  *                                                                      *
74  ************************************************************************/
75
76 /**
77  * xsltGenericErrorDefaultFunc:
78  * @ctx:  an error context
79  * @msg:  the message to display/transmit
80  * @...:  extra parameters for the message display
81  * 
82  * Default handler for out of context error messages.
83  */
84 void
85 xsltGenericErrorDefaultFunc(void *ctx, const char *msg, ...) {
86     va_list args;
87
88     if (xsltGenericErrorContext == NULL)
89         xsltGenericErrorContext = (void *) stderr;
90
91     va_start(args, msg);
92     vfprintf((FILE *)xsltGenericErrorContext, msg, args);
93     va_end(args);
94 }
95
96 xmlGenericErrorFunc xsltGenericError = xsltGenericErrorDefaultFunc;
97 void *xsltGenericErrorContext = NULL;
98
99
100 /**
101  * xsltSetGenericErrorFunc:
102  * @ctx:  the new error handling context
103  * @handler:  the new handler function
104  *
105  * Function to reset the handler and the error context for out of
106  * context error messages.
107  * This simply means that @handler will be called for subsequent
108  * error messages while not parsing nor validating. And @ctx will
109  * be passed as first argument to @handler
110  * One can simply force messages to be emitted to another FILE * than
111  * stderr by setting @ctx to this file handle and @handler to NULL.
112  */
113 void
114 xsltSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) {
115     xsltGenericErrorContext = ctx;
116     if (handler != NULL)
117         xsltGenericError = handler;
118     else
119         xsltGenericError = xsltGenericErrorDefaultFunc;
120 }
121
122 /**
123  * xsltGenericDebugDefaultFunc:
124  * @ctx:  an error context
125  * @msg:  the message to display/transmit
126  * @...:  extra parameters for the message display
127  * 
128  * Default handler for out of context error messages.
129  */
130 void
131 xsltGenericDebugDefaultFunc(void *ctx, const char *msg, ...) {
132     va_list args;
133
134     if (xsltGenericDebugContext == NULL)
135         return;
136
137     va_start(args, msg);
138     vfprintf((FILE *)xsltGenericDebugContext, msg, args);
139     va_end(args);
140 }
141
142 xmlGenericErrorFunc xsltGenericDebug = xsltGenericDebugDefaultFunc;
143 void *xsltGenericDebugContext = NULL;
144
145
146 /**
147  * xsltSetGenericDebugFunc:
148  * @ctx:  the new error handling context
149  * @handler:  the new handler function
150  *
151  * Function to reset the handler and the error context for out of
152  * context error messages.
153  * This simply means that @handler will be called for subsequent
154  * error messages while not parsing nor validating. And @ctx will
155  * be passed as first argument to @handler
156  * One can simply force messages to be emitted to another FILE * than
157  * stderr by setting @ctx to this file handle and @handler to NULL.
158  */
159 void
160 xsltSetGenericDebugFunc(void *ctx, xmlGenericErrorFunc handler) {
161     xsltGenericDebugContext = ctx;
162     if (handler != NULL)
163         xsltGenericDebug = handler;
164     else
165         xsltGenericDebug = xsltGenericDebugDefaultFunc;
166 }
167
168 /************************************************************************
169  *                                                                      *
170  *                              Sorting                                 *
171  *                                                                      *
172  ************************************************************************/
173
174 /**
175  * xsltSortFunction:
176  * @list:  the node set
177  * @results:  the results
178  * @descending:  direction of order
179  * @number:  the type of the result
180  *
181  * reorder the current node list @list accordingly to the values
182  * present in the array of results @results
183  */
184 void    
185 xsltSortFunction(xmlNodeSetPtr list, xmlXPathObjectPtr *results,
186                  int descending, int number) {
187     int i, j;
188     int len, tst;
189     xmlNodePtr node;
190     xmlXPathObjectPtr tmp;
191
192     if ((list == NULL) || (results == NULL))
193         return;
194     len = list->nodeNr;
195     if (len <= 1)
196         return;
197     /* TODO: sort is really not optimized, does it needs to ? */
198     for (i = 0;i < len -1;i++) {
199         for (j = i + 1; j < len; j++) {
200             if (results[i] == NULL)
201                 tst = 0;
202             else if (results[j] == NULL)
203                 tst = 1;
204             else if (number) {
205                 tst = (results[i]->floatval > results[j]->floatval);
206                 if (descending)
207                     tst = !tst;
208             } else {
209                 tst = xmlStrcmp(results[i]->stringval, results[j]->stringval);
210                 if (descending)
211                     tst = !tst;
212             }
213             if (tst) {
214                 tmp = results[i];
215                 results[i] = results[j];
216                 results[j] = tmp;
217                 node = list->nodeTab[i];
218                 list->nodeTab[i] = list->nodeTab[j];
219                 list->nodeTab[j] = node;
220             }
221         }
222     }
223 }
224
225 /************************************************************************
226  *                                                                      *
227  *                              Output                                  *
228  *                                                                      *
229  ************************************************************************/
230
231 /**
232  * xsltSaveResultTo:
233  * @buf:  an output buffer
234  * @result:  the result xmlDocPtr
235  * @style:  the stylesheet
236  *
237  * Save the result @result obtained by applying the @style stylesheet
238  * to an I/O output channel @buf
239  *
240  * Returns the number of byte written or -1 in case of failure.
241  */
242 int
243 xsltSaveResultTo(xmlOutputBufferPtr buf, xmlDocPtr result,
244                xsltStylesheetPtr style) {
245     const xmlChar *encoding;
246     xmlNodePtr root;
247     int base;
248
249     if ((buf == NULL) || (result == NULL) || (style == NULL))
250         return(-1);
251
252     if (style->methodURI != NULL) {
253         xsltGenericError(xsltGenericErrorContext,
254                 "xsltSaveResultTo : unknown ouput method\n");
255         return(-1);
256     }
257
258     /* TODO: when outputing and having imported stylesheets, apply cascade */
259     base = buf->written;
260     encoding = style->encoding;
261     if (style->method == NULL)
262         root = xmlDocGetRootElement(result);
263     else
264         root = NULL;
265     if (((style->method != NULL) &&
266          (xmlStrEqual(style->method, (const xmlChar *) "html"))) ||
267         ((root != NULL) &&
268          (xmlStrEqual(root->name, (const xmlChar *) "html")))){
269         htmlDocContentDumpOutput(buf, result, (const char *) encoding);
270     } else if ((style->method != NULL) &&
271                (xmlStrEqual(style->method, (const xmlChar *) "text"))) {
272         xmlNodePtr cur;
273
274         cur = result->children;
275         while (cur != NULL) {
276             if (cur->type == XML_TEXT_NODE)
277                 xmlOutputBufferWriteString(buf, (const char *) cur->content);
278             cur = cur->next;
279         }
280     } else {
281         if (style->omitXmlDeclaration != 1) {
282             xmlOutputBufferWriteString(buf, "<?xml version=");
283             if (result->version != NULL) 
284                 xmlBufferWriteQuotedString(buf->buffer, result->version);
285             else
286                 xmlOutputBufferWriteString(buf, "\"1.0\"");
287             if (encoding == NULL) {
288                 if (result->encoding != NULL)
289                     encoding = result->encoding;
290                 else if (result->charset != XML_CHAR_ENCODING_UTF8)
291                     encoding = (const xmlChar *)
292                                xmlGetCharEncodingName((xmlCharEncoding)
293                                                       result->charset);
294             }
295             if (encoding != NULL) {
296                 xmlOutputBufferWriteString(buf, " encoding=");
297                 xmlBufferWriteQuotedString(buf->buffer, (xmlChar *) encoding);
298             }
299             switch (style->standalone) {
300                 case 0:
301                     xmlOutputBufferWriteString(buf, " standalone=\"no\"");
302                     break;
303                 case 1:
304                     xmlOutputBufferWriteString(buf, " standalone=\"yes\"");
305                     break;
306                 default:
307                     break;
308             }
309             xmlOutputBufferWriteString(buf, "?>\n");
310         }
311         if (result->children != NULL) {
312             xmlNodePtr child = result->children;
313
314             while (child != NULL) {
315                 xmlNodeDumpOutput(buf, result, child, 0, (style->indent == 1),
316                                   (const char *) encoding);
317                 xmlOutputBufferWriteString(buf, "\n");
318                 child = child->next;
319             }
320         }
321         xmlOutputBufferFlush(buf);
322     }
323     return(buf->written - base);
324 }
325
326 /**
327  * xsltSaveResultToFilename:
328  * @URL:  a filename or URL
329  * @result:  the result xmlDocPtr
330  * @style:  the stylesheet
331  * @compression:  the compression factor (0 - 9 included)
332  *
333  * Save the result @result obtained by applying the @style stylesheet
334  * to a file or URL @URL
335  *
336  * Returns the number of byte written or -1 in case of failure.
337  */
338 int
339 xsltSaveResultToFilename(const char *URL, xmlDocPtr result,
340                          xsltStylesheetPtr style, int compression) {
341     xmlOutputBufferPtr buf;
342     int ret;
343
344     if ((URL == NULL) || (result == NULL) || (style == NULL))
345         return(-1);
346
347     buf = xmlOutputBufferCreateFilename(URL, NULL, compression);
348     if (buf == NULL)
349         return(-1);
350     xsltSaveResultTo(buf, result, style);
351     ret = xmlOutputBufferClose(buf);
352     return(ret);
353 }
354
355 /**
356  * xsltSaveResultToFile:
357  * @file:  a FILE * I/O
358  * @result:  the result xmlDocPtr
359  * @style:  the stylesheet
360  *
361  * Save the result @result obtained by applying the @style stylesheet
362  * to an open FILE * I/O.
363  * This does not close the FILE @file
364  *
365  * Returns the number of byte written or -1 in case of failure.
366  */
367 int
368 xsltSaveResultToFile(FILE *file, xmlDocPtr result, xsltStylesheetPtr style) {
369     xmlOutputBufferPtr buf;
370     int ret;
371
372     if ((file == NULL) || (result == NULL) || (style == NULL))
373         return(-1);
374
375     buf = xmlOutputBufferCreateFile(file, NULL);
376     if (buf == NULL)
377         return(-1);
378     xsltSaveResultTo(buf, result, style);
379     ret = xmlOutputBufferClose(buf);
380     return(ret);
381 }
382
383 /**
384  * xsltSaveResultToFd:
385  * @fd:  a file descriptor
386  * @result:  the result xmlDocPtr
387  * @style:  the stylesheet
388  *
389  * Save the result @result obtained by applying the @style stylesheet
390  * to an open file descriptor
391  * This does not close the descriptor.
392  *
393  * Returns the number of byte written or -1 in case of failure.
394  */
395 int
396 xsltSaveResultToFd(int fd, xmlDocPtr result, xsltStylesheetPtr style) {
397     xmlOutputBufferPtr buf;
398     int ret;
399
400     if ((fd < 0) || (result == NULL) || (style == NULL))
401         return(-1);
402
403     buf = xmlOutputBufferCreateFd(fd, NULL);
404     if (buf == NULL)
405         return(-1);
406     xsltSaveResultTo(buf, result, style);
407     ret = xmlOutputBufferClose(buf);
408     return(ret);
409 }
410