1 /*
2 * xmlsave.c: Implementation of the document serializer
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9 #define IN_LIBXML
10 #include "libxml.h"
11
12 #include <string.h>
13 #include <libxml/xmlmemory.h>
14 #include <libxml/parserInternals.h>
15 #include <libxml/tree.h>
16 #include <libxml/xmlsave.h>
17
18 #define MAX_INDENT 60
19
20 #include <libxml/HTMLtree.h>
21
22 #include "buf.h"
23 #include "enc.h"
24 #include "save.h"
25
26 /************************************************************************
27 * *
28 * XHTML detection *
29 * *
30 ************************************************************************/
31 #define XHTML_STRICT_PUBLIC_ID BAD_CAST \
32 "-//W3C//DTD XHTML 1.0 Strict//EN"
33 #define XHTML_STRICT_SYSTEM_ID BAD_CAST \
34 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
35 #define XHTML_FRAME_PUBLIC_ID BAD_CAST \
36 "-//W3C//DTD XHTML 1.0 Frameset//EN"
37 #define XHTML_FRAME_SYSTEM_ID BAD_CAST \
38 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"
39 #define XHTML_TRANS_PUBLIC_ID BAD_CAST \
40 "-//W3C//DTD XHTML 1.0 Transitional//EN"
41 #define XHTML_TRANS_SYSTEM_ID BAD_CAST \
42 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
43
44 #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml"
45 /**
46 * xmlIsXHTML:
47 * @systemID: the system identifier
48 * @publicID: the public identifier
49 *
50 * Try to find if the document correspond to an XHTML DTD
51 *
52 * Returns 1 if true, 0 if not and -1 in case of error
53 */
54 int
xmlIsXHTML(const xmlChar * systemID,const xmlChar * publicID)55 xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) {
56 if ((systemID == NULL) && (publicID == NULL))
57 return(-1);
58 if (publicID != NULL) {
59 if (xmlStrEqual(publicID, XHTML_STRICT_PUBLIC_ID)) return(1);
60 if (xmlStrEqual(publicID, XHTML_FRAME_PUBLIC_ID)) return(1);
61 if (xmlStrEqual(publicID, XHTML_TRANS_PUBLIC_ID)) return(1);
62 }
63 if (systemID != NULL) {
64 if (xmlStrEqual(systemID, XHTML_STRICT_SYSTEM_ID)) return(1);
65 if (xmlStrEqual(systemID, XHTML_FRAME_SYSTEM_ID)) return(1);
66 if (xmlStrEqual(systemID, XHTML_TRANS_SYSTEM_ID)) return(1);
67 }
68 return(0);
69 }
70
71 #ifdef LIBXML_OUTPUT_ENABLED
72
73 #define TODO \
74 xmlGenericError(xmlGenericErrorContext, \
75 "Unimplemented block at %s:%d\n", \
76 __FILE__, __LINE__);
77
78 struct _xmlSaveCtxt {
79 void *_private;
80 int type;
81 int fd;
82 const xmlChar *filename;
83 const xmlChar *encoding;
84 xmlCharEncodingHandlerPtr handler;
85 xmlOutputBufferPtr buf;
86 int options;
87 int level;
88 int format;
89 char indent[MAX_INDENT + 1]; /* array for indenting output */
90 int indent_nr;
91 int indent_size;
92 xmlCharEncodingOutputFunc escape; /* used for element content */
93 xmlCharEncodingOutputFunc escapeAttr;/* used for attribute content */
94 };
95
96 /************************************************************************
97 * *
98 * Output error handlers *
99 * *
100 ************************************************************************/
101 /**
102 * xmlSaveErrMemory:
103 * @extra: extra informations
104 *
105 * Handle an out of memory condition
106 */
107 static void
xmlSaveErrMemory(const char * extra)108 xmlSaveErrMemory(const char *extra)
109 {
110 __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra);
111 }
112
113 /**
114 * xmlSaveErr:
115 * @code: the error number
116 * @node: the location of the error.
117 * @extra: extra informations
118 *
119 * Handle an out of memory condition
120 */
121 static void
xmlSaveErr(int code,xmlNodePtr node,const char * extra)122 xmlSaveErr(int code, xmlNodePtr node, const char *extra)
123 {
124 const char *msg = NULL;
125
126 switch(code) {
127 case XML_SAVE_NOT_UTF8:
128 msg = "string is not in UTF-8\n";
129 break;
130 case XML_SAVE_CHAR_INVALID:
131 msg = "invalid character value\n";
132 break;
133 case XML_SAVE_UNKNOWN_ENCODING:
134 msg = "unknown encoding %s\n";
135 break;
136 case XML_SAVE_NO_DOCTYPE:
137 msg = "document has no DOCTYPE\n";
138 break;
139 default:
140 msg = "unexpected error number\n";
141 }
142 __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra);
143 }
144
145 /************************************************************************
146 * *
147 * Special escaping routines *
148 * *
149 ************************************************************************/
150 static unsigned char *
xmlSerializeHexCharRef(unsigned char * out,int val)151 xmlSerializeHexCharRef(unsigned char *out, int val) {
152 unsigned char *ptr;
153
154 *out++ = '&';
155 *out++ = '#';
156 *out++ = 'x';
157 if (val < 0x10) ptr = out;
158 else if (val < 0x100) ptr = out + 1;
159 else if (val < 0x1000) ptr = out + 2;
160 else if (val < 0x10000) ptr = out + 3;
161 else if (val < 0x100000) ptr = out + 4;
162 else ptr = out + 5;
163 out = ptr + 1;
164 while (val > 0) {
165 switch (val & 0xF) {
166 case 0: *ptr-- = '0'; break;
167 case 1: *ptr-- = '1'; break;
168 case 2: *ptr-- = '2'; break;
169 case 3: *ptr-- = '3'; break;
170 case 4: *ptr-- = '4'; break;
171 case 5: *ptr-- = '5'; break;
172 case 6: *ptr-- = '6'; break;
173 case 7: *ptr-- = '7'; break;
174 case 8: *ptr-- = '8'; break;
175 case 9: *ptr-- = '9'; break;
176 case 0xA: *ptr-- = 'A'; break;
177 case 0xB: *ptr-- = 'B'; break;
178 case 0xC: *ptr-- = 'C'; break;
179 case 0xD: *ptr-- = 'D'; break;
180 case 0xE: *ptr-- = 'E'; break;
181 case 0xF: *ptr-- = 'F'; break;
182 default: *ptr-- = '0'; break;
183 }
184 val >>= 4;
185 }
186 *out++ = ';';
187 *out = 0;
188 return(out);
189 }
190
191 /**
192 * xmlEscapeEntities:
193 * @out: a pointer to an array of bytes to store the result
194 * @outlen: the length of @out
195 * @in: a pointer to an array of unescaped UTF-8 bytes
196 * @inlen: the length of @in
197 *
198 * Take a block of UTF-8 chars in and escape them. Used when there is no
199 * encoding specified.
200 *
201 * Returns 0 if success, or -1 otherwise
202 * The value of @inlen after return is the number of octets consumed
203 * if the return value is positive, else unpredictable.
204 * The value of @outlen after return is the number of octets consumed.
205 */
206 static int
xmlEscapeEntities(unsigned char * out,int * outlen,const xmlChar * in,int * inlen)207 xmlEscapeEntities(unsigned char* out, int *outlen,
208 const xmlChar* in, int *inlen) {
209 unsigned char* outstart = out;
210 const unsigned char* base = in;
211 unsigned char* outend = out + *outlen;
212 const unsigned char* inend;
213 int val;
214
215 inend = in + (*inlen);
216
217 while ((in < inend) && (out < outend)) {
218 if (*in == '<') {
219 if (outend - out < 4) break;
220 *out++ = '&';
221 *out++ = 'l';
222 *out++ = 't';
223 *out++ = ';';
224 in++;
225 continue;
226 } else if (*in == '>') {
227 if (outend - out < 4) break;
228 *out++ = '&';
229 *out++ = 'g';
230 *out++ = 't';
231 *out++ = ';';
232 in++;
233 continue;
234 } else if (*in == '&') {
235 if (outend - out < 5) break;
236 *out++ = '&';
237 *out++ = 'a';
238 *out++ = 'm';
239 *out++ = 'p';
240 *out++ = ';';
241 in++;
242 continue;
243 } else if (((*in >= 0x20) && (*in < 0x80)) ||
244 (*in == '\n') || (*in == '\t')) {
245 /*
246 * default case, just copy !
247 */
248 *out++ = *in++;
249 continue;
250 } else if (*in >= 0x80) {
251 /*
252 * We assume we have UTF-8 input.
253 */
254 if (outend - out < 11) break;
255
256 if (*in < 0xC0) {
257 xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL);
258 in++;
259 goto error;
260 } else if (*in < 0xE0) {
261 if (inend - in < 2) break;
262 val = (in[0]) & 0x1F;
263 val <<= 6;
264 val |= (in[1]) & 0x3F;
265 in += 2;
266 } else if (*in < 0xF0) {
267 if (inend - in < 3) break;
268 val = (in[0]) & 0x0F;
269 val <<= 6;
270 val |= (in[1]) & 0x3F;
271 val <<= 6;
272 val |= (in[2]) & 0x3F;
273 in += 3;
274 } else if (*in < 0xF8) {
275 if (inend - in < 4) break;
276 val = (in[0]) & 0x07;
277 val <<= 6;
278 val |= (in[1]) & 0x3F;
279 val <<= 6;
280 val |= (in[2]) & 0x3F;
281 val <<= 6;
282 val |= (in[3]) & 0x3F;
283 in += 4;
284 } else {
285 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
286 in++;
287 goto error;
288 }
289 if (!IS_CHAR(val)) {
290 xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL);
291 in++;
292 goto error;
293 }
294
295 /*
296 * We could do multiple things here. Just save as a char ref
297 */
298 out = xmlSerializeHexCharRef(out, val);
299 } else if (IS_BYTE_CHAR(*in)) {
300 if (outend - out < 6) break;
301 out = xmlSerializeHexCharRef(out, *in++);
302 } else {
303 xmlGenericError(xmlGenericErrorContext,
304 "xmlEscapeEntities : char out of range\n");
305 in++;
306 goto error;
307 }
308 }
309 *outlen = out - outstart;
310 *inlen = in - base;
311 return(0);
312 error:
313 *outlen = out - outstart;
314 *inlen = in - base;
315 return(-1);
316 }
317
318 /************************************************************************
319 * *
320 * Allocation and deallocation *
321 * *
322 ************************************************************************/
323 /**
324 * xmlSaveCtxtInit:
325 * @ctxt: the saving context
326 *
327 * Initialize a saving context
328 */
329 static void
xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)330 xmlSaveCtxtInit(xmlSaveCtxtPtr ctxt)
331 {
332 int i;
333 int len;
334
335 if (ctxt == NULL) return;
336 if ((ctxt->encoding == NULL) && (ctxt->escape == NULL))
337 ctxt->escape = xmlEscapeEntities;
338 len = xmlStrlen((xmlChar *)xmlTreeIndentString);
339 if ((xmlTreeIndentString == NULL) || (len == 0)) {
340 memset(&ctxt->indent[0], 0, MAX_INDENT + 1);
341 } else {
342 ctxt->indent_size = len;
343 ctxt->indent_nr = MAX_INDENT / ctxt->indent_size;
344 for (i = 0;i < ctxt->indent_nr;i++)
345 memcpy(&ctxt->indent[i * ctxt->indent_size], xmlTreeIndentString,
346 ctxt->indent_size);
347 ctxt->indent[ctxt->indent_nr * ctxt->indent_size] = 0;
348 }
349
350 if (xmlSaveNoEmptyTags) {
351 ctxt->options |= XML_SAVE_NO_EMPTY;
352 }
353 }
354
355 /**
356 * xmlFreeSaveCtxt:
357 *
358 * Free a saving context, destroying the output in any remaining buffer
359 */
360 static void
xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)361 xmlFreeSaveCtxt(xmlSaveCtxtPtr ctxt)
362 {
363 if (ctxt == NULL) return;
364 if (ctxt->encoding != NULL)
365 xmlFree((char *) ctxt->encoding);
366 if (ctxt->buf != NULL)
367 xmlOutputBufferClose(ctxt->buf);
368 xmlFree(ctxt);
369 }
370
371 /**
372 * xmlNewSaveCtxt:
373 *
374 * Create a new saving context
375 *
376 * Returns the new structure or NULL in case of error
377 */
378 static xmlSaveCtxtPtr
xmlNewSaveCtxt(const char * encoding,int options)379 xmlNewSaveCtxt(const char *encoding, int options)
380 {
381 xmlSaveCtxtPtr ret;
382
383 ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt));
384 if (ret == NULL) {
385 xmlSaveErrMemory("creating saving context");
386 return ( NULL );
387 }
388 memset(ret, 0, sizeof(xmlSaveCtxt));
389
390 if (encoding != NULL) {
391 ret->handler = xmlFindCharEncodingHandler(encoding);
392 if (ret->handler == NULL) {
393 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding);
394 xmlFreeSaveCtxt(ret);
395 return(NULL);
396 }
397 ret->encoding = xmlStrdup((const xmlChar *)encoding);
398 ret->escape = NULL;
399 }
400 xmlSaveCtxtInit(ret);
401
402 /*
403 * Use the options
404 */
405
406 /* Re-check this option as it may already have been set */
407 if ((ret->options & XML_SAVE_NO_EMPTY) && ! (options & XML_SAVE_NO_EMPTY)) {
408 options |= XML_SAVE_NO_EMPTY;
409 }
410
411 ret->options = options;
412 if (options & XML_SAVE_FORMAT)
413 ret->format = 1;
414 else if (options & XML_SAVE_WSNONSIG)
415 ret->format = 2;
416
417 return(ret);
418 }
419
420 /************************************************************************
421 * *
422 * Dumping XML tree content to a simple buffer *
423 * *
424 ************************************************************************/
425 /**
426 * xmlAttrSerializeContent:
427 * @buf: the XML buffer output
428 * @doc: the document
429 * @attr: the attribute pointer
430 *
431 * Serialize the attribute in the buffer
432 */
433 static void
xmlAttrSerializeContent(xmlOutputBufferPtr buf,xmlAttrPtr attr)434 xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr)
435 {
436 xmlNodePtr children;
437
438 children = attr->children;
439 while (children != NULL) {
440 switch (children->type) {
441 case XML_TEXT_NODE:
442 xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc,
443 attr, children->content);
444 break;
445 case XML_ENTITY_REF_NODE:
446 xmlBufAdd(buf->buffer, BAD_CAST "&", 1);
447 xmlBufAdd(buf->buffer, children->name,
448 xmlStrlen(children->name));
449 xmlBufAdd(buf->buffer, BAD_CAST ";", 1);
450 break;
451 default:
452 /* should not happen unless we have a badly built tree */
453 break;
454 }
455 children = children->next;
456 }
457 }
458
459 /**
460 * xmlBufDumpNotationTable:
461 * @buf: an xmlBufPtr output
462 * @table: A notation table
463 *
464 * This will dump the content of the notation table as an XML DTD definition
465 */
466 void
xmlBufDumpNotationTable(xmlBufPtr buf,xmlNotationTablePtr table)467 xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) {
468 xmlBufferPtr buffer;
469
470 buffer = xmlBufferCreate();
471 if (buffer == NULL) {
472 /*
473 * TODO set the error in buf
474 */
475 return;
476 }
477 xmlDumpNotationTable(buffer, table);
478 xmlBufMergeBuffer(buf, buffer);
479 }
480
481 /**
482 * xmlBufDumpElementDecl:
483 * @buf: an xmlBufPtr output
484 * @elem: An element table
485 *
486 * This will dump the content of the element declaration as an XML
487 * DTD definition
488 */
489 void
xmlBufDumpElementDecl(xmlBufPtr buf,xmlElementPtr elem)490 xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) {
491 xmlBufferPtr buffer;
492
493 buffer = xmlBufferCreate();
494 if (buffer == NULL) {
495 /*
496 * TODO set the error in buf
497 */
498 return;
499 }
500 xmlDumpElementDecl(buffer, elem);
501 xmlBufMergeBuffer(buf, buffer);
502 }
503
504 /**
505 * xmlBufDumpAttributeDecl:
506 * @buf: an xmlBufPtr output
507 * @attr: An attribute declaration
508 *
509 * This will dump the content of the attribute declaration as an XML
510 * DTD definition
511 */
512 void
xmlBufDumpAttributeDecl(xmlBufPtr buf,xmlAttributePtr attr)513 xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) {
514 xmlBufferPtr buffer;
515
516 buffer = xmlBufferCreate();
517 if (buffer == NULL) {
518 /*
519 * TODO set the error in buf
520 */
521 return;
522 }
523 xmlDumpAttributeDecl(buffer, attr);
524 xmlBufMergeBuffer(buf, buffer);
525 }
526
527 /**
528 * xmlBufDumpEntityDecl:
529 * @buf: an xmlBufPtr output
530 * @ent: An entity table
531 *
532 * This will dump the content of the entity table as an XML DTD definition
533 */
534 void
xmlBufDumpEntityDecl(xmlBufPtr buf,xmlEntityPtr ent)535 xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) {
536 xmlBufferPtr buffer;
537
538 buffer = xmlBufferCreate();
539 if (buffer == NULL) {
540 /*
541 * TODO set the error in buf
542 */
543 return;
544 }
545 xmlDumpEntityDecl(buffer, ent);
546 xmlBufMergeBuffer(buf, buffer);
547 }
548
549 /************************************************************************
550 * *
551 * Dumping XML tree content to an I/O output buffer *
552 * *
553 ************************************************************************/
554
xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt,const char * encoding)555 static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) {
556 xmlOutputBufferPtr buf = ctxt->buf;
557
558 if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) {
559 buf->encoder = xmlFindCharEncodingHandler((const char *)encoding);
560 if (buf->encoder == NULL) {
561 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL,
562 (const char *)encoding);
563 return(-1);
564 }
565 buf->conv = xmlBufCreate();
566 if (buf->conv == NULL) {
567 xmlCharEncCloseFunc(buf->encoder);
568 xmlSaveErrMemory("creating encoding buffer");
569 return(-1);
570 }
571 /*
572 * initialize the state, e.g. if outputting a BOM
573 */
574 xmlCharEncOutput(buf, 1);
575 }
576 return(0);
577 }
578
xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt)579 static int xmlSaveClearEncoding(xmlSaveCtxtPtr ctxt) {
580 xmlOutputBufferPtr buf = ctxt->buf;
581 xmlOutputBufferFlush(buf);
582 xmlCharEncCloseFunc(buf->encoder);
583 xmlBufFree(buf->conv);
584 buf->encoder = NULL;
585 buf->conv = NULL;
586 return(0);
587 }
588
589 #ifdef LIBXML_HTML_ENABLED
590 static void
591 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
592 #endif
593 static void xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
594 static void xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
595 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
596 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
597
598 /**
599 * xmlOutputBufferWriteWSNonSig:
600 * @ctxt: The save context
601 * @extra: Number of extra indents to apply to ctxt->level
602 *
603 * Write out formatting for non-significant whitespace output.
604 */
605 static void
xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt,int extra)606 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
607 {
608 int i;
609 if ((ctxt == NULL) || (ctxt->buf == NULL))
610 return;
611 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
612 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
613 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
614 ((ctxt->level + extra - i) > ctxt->indent_nr ?
615 ctxt->indent_nr : (ctxt->level + extra - i)),
616 ctxt->indent);
617 }
618 }
619
620 /**
621 * xmlNsDumpOutput:
622 * @buf: the XML buffer output
623 * @cur: a namespace
624 * @ctxt: the output save context. Optional.
625 *
626 * Dump a local Namespace definition.
627 * Should be called in the context of attributes dumps.
628 * If @ctxt is supplied, @buf should be its buffer.
629 */
630 static void
xmlNsDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur,xmlSaveCtxtPtr ctxt)631 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
632 if ((cur == NULL) || (buf == NULL)) return;
633 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
634 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
635 return;
636
637 if (ctxt != NULL && ctxt->format == 2)
638 xmlOutputBufferWriteWSNonSig(ctxt, 2);
639 else
640 xmlOutputBufferWrite(buf, 1, " ");
641
642 /* Within the context of an element attributes */
643 if (cur->prefix != NULL) {
644 xmlOutputBufferWrite(buf, 6, "xmlns:");
645 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
646 } else
647 xmlOutputBufferWrite(buf, 5, "xmlns");
648 xmlOutputBufferWrite(buf, 1, "=");
649 xmlBufWriteQuotedString(buf->buffer, cur->href);
650 }
651 }
652
653 /**
654 * xmlNsDumpOutputCtxt
655 * @ctxt: the save context
656 * @cur: a namespace
657 *
658 * Dump a local Namespace definition to a save context.
659 * Should be called in the context of attribute dumps.
660 */
661 static void
xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)662 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
663 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
664 }
665
666 /**
667 * xmlNsListDumpOutputCtxt
668 * @ctxt: the save context
669 * @cur: the first namespace
670 *
671 * Dump a list of local namespace definitions to a save context.
672 * Should be called in the context of attribute dumps.
673 */
674 static void
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)675 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
676 while (cur != NULL) {
677 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
678 cur = cur->next;
679 }
680 }
681
682 /**
683 * xmlNsListDumpOutput:
684 * @buf: the XML buffer output
685 * @cur: the first namespace
686 *
687 * Dump a list of local Namespace definitions.
688 * Should be called in the context of attributes dumps.
689 */
690 void
xmlNsListDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur)691 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
692 while (cur != NULL) {
693 xmlNsDumpOutput(buf, cur, NULL);
694 cur = cur->next;
695 }
696 }
697
698 /**
699 * xmlDtdDumpOutput:
700 * @buf: the XML buffer output
701 * @dtd: the pointer to the DTD
702 *
703 * Dump the XML document DTD, if any.
704 */
705 static void
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt,xmlDtdPtr dtd)706 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
707 xmlOutputBufferPtr buf;
708 int format, level;
709
710 if (dtd == NULL) return;
711 if ((ctxt == NULL) || (ctxt->buf == NULL))
712 return;
713 buf = ctxt->buf;
714 xmlOutputBufferWrite(buf, 10, "<!DOCTYPE ");
715 xmlOutputBufferWriteString(buf, (const char *)dtd->name);
716 if (dtd->ExternalID != NULL) {
717 xmlOutputBufferWrite(buf, 8, " PUBLIC ");
718 xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID);
719 xmlOutputBufferWrite(buf, 1, " ");
720 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
721 } else if (dtd->SystemID != NULL) {
722 xmlOutputBufferWrite(buf, 8, " SYSTEM ");
723 xmlBufWriteQuotedString(buf->buffer, dtd->SystemID);
724 }
725 if ((dtd->entities == NULL) && (dtd->elements == NULL) &&
726 (dtd->attributes == NULL) && (dtd->notations == NULL) &&
727 (dtd->pentities == NULL)) {
728 xmlOutputBufferWrite(buf, 1, ">");
729 return;
730 }
731 xmlOutputBufferWrite(buf, 3, " [\n");
732 /*
733 * Dump the notations first they are not in the DTD children list
734 * Do this only on a standalone DTD or on the internal subset though.
735 */
736 if ((dtd->notations != NULL) && ((dtd->doc == NULL) ||
737 (dtd->doc->intSubset == dtd))) {
738 xmlBufDumpNotationTable(buf->buffer,
739 (xmlNotationTablePtr) dtd->notations);
740 }
741 format = ctxt->format;
742 level = ctxt->level;
743 ctxt->format = 0;
744 ctxt->level = -1;
745 xmlNodeListDumpOutput(ctxt, dtd->children);
746 ctxt->format = format;
747 ctxt->level = level;
748 xmlOutputBufferWrite(buf, 2, "]>");
749 }
750
751 /**
752 * xmlAttrDumpOutput:
753 * @buf: the XML buffer output
754 * @cur: the attribute pointer
755 *
756 * Dump an XML attribute
757 */
758 static void
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)759 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
760 xmlOutputBufferPtr buf;
761
762 if (cur == NULL) return;
763 buf = ctxt->buf;
764 if (buf == NULL) return;
765 if (ctxt->format == 2)
766 xmlOutputBufferWriteWSNonSig(ctxt, 2);
767 else
768 xmlOutputBufferWrite(buf, 1, " ");
769 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
770 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
771 xmlOutputBufferWrite(buf, 1, ":");
772 }
773 xmlOutputBufferWriteString(buf, (const char *)cur->name);
774 xmlOutputBufferWrite(buf, 2, "=\"");
775 xmlAttrSerializeContent(buf, cur);
776 xmlOutputBufferWrite(buf, 1, "\"");
777 }
778
779 /**
780 * xmlAttrListDumpOutput:
781 * @buf: the XML buffer output
782 * @doc: the document
783 * @cur: the first attribute pointer
784 * @encoding: an optional encoding string
785 *
786 * Dump a list of XML attributes
787 */
788 static void
xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)789 xmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
790 if (cur == NULL) return;
791 while (cur != NULL) {
792 xmlAttrDumpOutput(ctxt, cur);
793 cur = cur->next;
794 }
795 }
796
797
798
799 /**
800 * xmlNodeListDumpOutput:
801 * @cur: the first node
802 *
803 * Dump an XML node list, recursive behaviour, children are printed too.
804 */
805 static void
xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)806 xmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
807 xmlOutputBufferPtr buf;
808
809 if (cur == NULL) return;
810 buf = ctxt->buf;
811 while (cur != NULL) {
812 if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
813 ((cur->type == XML_ELEMENT_NODE) ||
814 (cur->type == XML_COMMENT_NODE) ||
815 (cur->type == XML_PI_NODE)))
816 xmlOutputBufferWrite(buf, ctxt->indent_size *
817 (ctxt->level > ctxt->indent_nr ?
818 ctxt->indent_nr : ctxt->level),
819 ctxt->indent);
820 xmlNodeDumpOutputInternal(ctxt, cur);
821 if (ctxt->format == 1) {
822 xmlOutputBufferWrite(buf, 1, "\n");
823 }
824 cur = cur->next;
825 }
826 }
827
828 #ifdef LIBXML_HTML_ENABLED
829 /**
830 * xmlNodeDumpOutputInternal:
831 * @cur: the current node
832 *
833 * Dump an HTML node, recursive behaviour, children are printed too.
834 */
835 static int
htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)836 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
837 const xmlChar *oldenc = NULL;
838 const xmlChar *oldctxtenc = ctxt->encoding;
839 const xmlChar *encoding = ctxt->encoding;
840 xmlOutputBufferPtr buf = ctxt->buf;
841 int switched_encoding = 0;
842 xmlDocPtr doc;
843
844 xmlInitParser();
845
846 doc = cur->doc;
847 if (doc != NULL) {
848 oldenc = doc->encoding;
849 if (ctxt->encoding != NULL) {
850 doc->encoding = BAD_CAST ctxt->encoding;
851 } else if (doc->encoding != NULL) {
852 encoding = doc->encoding;
853 }
854 }
855
856 if ((encoding != NULL) && (doc != NULL))
857 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
858 if ((encoding == NULL) && (doc != NULL))
859 encoding = htmlGetMetaEncoding(doc);
860 if (encoding == NULL)
861 encoding = BAD_CAST "HTML";
862 if ((encoding != NULL) && (oldctxtenc == NULL) &&
863 (buf->encoder == NULL) && (buf->conv == NULL)) {
864 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
865 doc->encoding = oldenc;
866 return(-1);
867 }
868 switched_encoding = 1;
869 }
870 if (ctxt->options & XML_SAVE_FORMAT)
871 htmlNodeDumpFormatOutput(buf, doc, cur,
872 (const char *)encoding, 1);
873 else
874 htmlNodeDumpFormatOutput(buf, doc, cur,
875 (const char *)encoding, 0);
876 /*
877 * Restore the state of the saving context at the end of the document
878 */
879 if ((switched_encoding) && (oldctxtenc == NULL)) {
880 xmlSaveClearEncoding(ctxt);
881 }
882 if (doc != NULL)
883 doc->encoding = oldenc;
884 return(0);
885 }
886 #endif
887
888 /**
889 * xmlNodeDumpOutputInternal:
890 * @cur: the current node
891 *
892 * Dump an XML node, recursive behaviour, children are printed too.
893 */
894 static void
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)895 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
896 int format;
897 xmlNodePtr tmp;
898 xmlChar *start, *end;
899 xmlOutputBufferPtr buf;
900
901 if (cur == NULL) return;
902 buf = ctxt->buf;
903 if (cur->type == XML_XINCLUDE_START)
904 return;
905 if (cur->type == XML_XINCLUDE_END)
906 return;
907 if ((cur->type == XML_DOCUMENT_NODE) ||
908 (cur->type == XML_HTML_DOCUMENT_NODE)) {
909 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
910 return;
911 }
912 #ifdef LIBXML_HTML_ENABLED
913 if (ctxt->options & XML_SAVE_XHTML) {
914 xhtmlNodeDumpOutput(ctxt, cur);
915 return;
916 }
917 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
918 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
919 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
920 (ctxt->options & XML_SAVE_AS_HTML)) {
921 htmlNodeDumpOutputInternal(ctxt, cur);
922 return;
923 }
924 #endif
925 if (cur->type == XML_DTD_NODE) {
926 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
927 return;
928 }
929 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
930 xmlNodeListDumpOutput(ctxt, cur->children);
931 return;
932 }
933 if (cur->type == XML_ELEMENT_DECL) {
934 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
935 return;
936 }
937 if (cur->type == XML_ATTRIBUTE_DECL) {
938 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
939 return;
940 }
941 if (cur->type == XML_ENTITY_DECL) {
942 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
943 return;
944 }
945 if (cur->type == XML_TEXT_NODE) {
946 if (cur->content != NULL) {
947 if (cur->name != xmlStringTextNoenc) {
948 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
949 } else {
950 /*
951 * Disable escaping, needed for XSLT
952 */
953 xmlOutputBufferWriteString(buf, (const char *) cur->content);
954 }
955 }
956
957 return;
958 }
959 if (cur->type == XML_PI_NODE) {
960 if (cur->content != NULL) {
961 xmlOutputBufferWrite(buf, 2, "<?");
962 xmlOutputBufferWriteString(buf, (const char *)cur->name);
963 if (cur->content != NULL) {
964 if (ctxt->format == 2)
965 xmlOutputBufferWriteWSNonSig(ctxt, 0);
966 else
967 xmlOutputBufferWrite(buf, 1, " ");
968 xmlOutputBufferWriteString(buf, (const char *)cur->content);
969 }
970 xmlOutputBufferWrite(buf, 2, "?>");
971 } else {
972 xmlOutputBufferWrite(buf, 2, "<?");
973 xmlOutputBufferWriteString(buf, (const char *)cur->name);
974 if (ctxt->format == 2)
975 xmlOutputBufferWriteWSNonSig(ctxt, 0);
976 xmlOutputBufferWrite(buf, 2, "?>");
977 }
978 return;
979 }
980 if (cur->type == XML_COMMENT_NODE) {
981 if (cur->content != NULL) {
982 xmlOutputBufferWrite(buf, 4, "<!--");
983 xmlOutputBufferWriteString(buf, (const char *)cur->content);
984 xmlOutputBufferWrite(buf, 3, "-->");
985 }
986 return;
987 }
988 if (cur->type == XML_ENTITY_REF_NODE) {
989 xmlOutputBufferWrite(buf, 1, "&");
990 xmlOutputBufferWriteString(buf, (const char *)cur->name);
991 xmlOutputBufferWrite(buf, 1, ";");
992 return;
993 }
994 if (cur->type == XML_CDATA_SECTION_NODE) {
995 if (cur->content == NULL || *cur->content == '\0') {
996 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
997 } else {
998 start = end = cur->content;
999 while (*end != '\0') {
1000 if ((*end == ']') && (*(end + 1) == ']') &&
1001 (*(end + 2) == '>')) {
1002 end = end + 2;
1003 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1004 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1005 xmlOutputBufferWrite(buf, 3, "]]>");
1006 start = end;
1007 }
1008 end++;
1009 }
1010 if (start != end) {
1011 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1012 xmlOutputBufferWriteString(buf, (const char *)start);
1013 xmlOutputBufferWrite(buf, 3, "]]>");
1014 }
1015 }
1016 return;
1017 }
1018 if (cur->type == XML_ATTRIBUTE_NODE) {
1019 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1020 return;
1021 }
1022 if (cur->type == XML_NAMESPACE_DECL) {
1023 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1024 return;
1025 }
1026
1027 format = ctxt->format;
1028 if (format == 1) {
1029 tmp = cur->children;
1030 while (tmp != NULL) {
1031 if ((tmp->type == XML_TEXT_NODE) ||
1032 (tmp->type == XML_CDATA_SECTION_NODE) ||
1033 (tmp->type == XML_ENTITY_REF_NODE)) {
1034 ctxt->format = 0;
1035 break;
1036 }
1037 tmp = tmp->next;
1038 }
1039 }
1040 xmlOutputBufferWrite(buf, 1, "<");
1041 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1042 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1043 xmlOutputBufferWrite(buf, 1, ":");
1044 }
1045
1046 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1047 if (cur->nsDef)
1048 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1049 if (cur->properties != NULL)
1050 xmlAttrListDumpOutput(ctxt, cur->properties);
1051
1052 if (((cur->type == XML_ELEMENT_NODE) || (cur->content == NULL)) &&
1053 (cur->children == NULL) && ((ctxt->options & XML_SAVE_NO_EMPTY) == 0)) {
1054 if (ctxt->format == 2)
1055 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1056 xmlOutputBufferWrite(buf, 2, "/>");
1057 ctxt->format = format;
1058 return;
1059 }
1060 if (ctxt->format == 2)
1061 xmlOutputBufferWriteWSNonSig(ctxt, 1);
1062 xmlOutputBufferWrite(buf, 1, ">");
1063 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1064 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1065 }
1066 if (cur->children != NULL) {
1067 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1068 if (ctxt->level >= 0) ctxt->level++;
1069 xmlNodeListDumpOutput(ctxt, cur->children);
1070 if (ctxt->level > 0) ctxt->level--;
1071 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1072 xmlOutputBufferWrite(buf, ctxt->indent_size *
1073 (ctxt->level > ctxt->indent_nr ?
1074 ctxt->indent_nr : ctxt->level),
1075 ctxt->indent);
1076 }
1077 xmlOutputBufferWrite(buf, 2, "</");
1078 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1079 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1080 xmlOutputBufferWrite(buf, 1, ":");
1081 }
1082
1083 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1084 if (ctxt->format == 2)
1085 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1086 xmlOutputBufferWrite(buf, 1, ">");
1087 ctxt->format = format;
1088 }
1089
1090 /**
1091 * xmlDocContentDumpOutput:
1092 * @cur: the document
1093 *
1094 * Dump an XML document.
1095 */
1096 static int
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt,xmlDocPtr cur)1097 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1098 #ifdef LIBXML_HTML_ENABLED
1099 xmlDtdPtr dtd;
1100 int is_xhtml = 0;
1101 #endif
1102 const xmlChar *oldenc = cur->encoding;
1103 const xmlChar *oldctxtenc = ctxt->encoding;
1104 const xmlChar *encoding = ctxt->encoding;
1105 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1106 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1107 xmlOutputBufferPtr buf = ctxt->buf;
1108 xmlCharEncoding enc;
1109 int switched_encoding = 0;
1110
1111 xmlInitParser();
1112
1113 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1114 (cur->type != XML_DOCUMENT_NODE))
1115 return(-1);
1116
1117 if (ctxt->encoding != NULL) {
1118 cur->encoding = BAD_CAST ctxt->encoding;
1119 } else if (cur->encoding != NULL) {
1120 encoding = cur->encoding;
1121 }
1122
1123 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1124 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1125 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1126 (ctxt->options & XML_SAVE_AS_HTML)) {
1127 #ifdef LIBXML_HTML_ENABLED
1128 if (encoding != NULL)
1129 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1130 if (encoding == NULL)
1131 encoding = htmlGetMetaEncoding(cur);
1132 if (encoding == NULL)
1133 encoding = BAD_CAST "HTML";
1134 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1135 (buf->encoder == NULL) && (buf->conv == NULL)) {
1136 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1137 cur->encoding = oldenc;
1138 return(-1);
1139 }
1140 }
1141 if (ctxt->options & XML_SAVE_FORMAT)
1142 htmlDocContentDumpFormatOutput(buf, cur,
1143 (const char *)encoding, 1);
1144 else
1145 htmlDocContentDumpFormatOutput(buf, cur,
1146 (const char *)encoding, 0);
1147 if (ctxt->encoding != NULL)
1148 cur->encoding = oldenc;
1149 return(0);
1150 #else
1151 return(-1);
1152 #endif
1153 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1154 (ctxt->options & XML_SAVE_AS_XML) ||
1155 (ctxt->options & XML_SAVE_XHTML)) {
1156 enc = xmlParseCharEncoding((const char*) encoding);
1157 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1158 (buf->encoder == NULL) && (buf->conv == NULL) &&
1159 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1160 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1161 (enc != XML_CHAR_ENCODING_NONE) &&
1162 (enc != XML_CHAR_ENCODING_ASCII)) {
1163 /*
1164 * we need to switch to this encoding but just for this
1165 * document since we output the XMLDecl the conversion
1166 * must be done to not generate not well formed documents.
1167 */
1168 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1169 cur->encoding = oldenc;
1170 return(-1);
1171 }
1172 switched_encoding = 1;
1173 }
1174 if (ctxt->escape == xmlEscapeEntities)
1175 ctxt->escape = NULL;
1176 if (ctxt->escapeAttr == xmlEscapeEntities)
1177 ctxt->escapeAttr = NULL;
1178 }
1179
1180
1181 /*
1182 * Save the XML declaration
1183 */
1184 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1185 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1186 if (cur->version != NULL)
1187 xmlBufWriteQuotedString(buf->buffer, cur->version);
1188 else
1189 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1190 if (encoding != NULL) {
1191 xmlOutputBufferWrite(buf, 10, " encoding=");
1192 xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1193 }
1194 switch (cur->standalone) {
1195 case 0:
1196 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1197 break;
1198 case 1:
1199 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1200 break;
1201 }
1202 xmlOutputBufferWrite(buf, 3, "?>\n");
1203 }
1204
1205 #ifdef LIBXML_HTML_ENABLED
1206 if (ctxt->options & XML_SAVE_XHTML)
1207 is_xhtml = 1;
1208 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1209 dtd = xmlGetIntSubset(cur);
1210 if (dtd != NULL) {
1211 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1212 if (is_xhtml < 0) is_xhtml = 0;
1213 }
1214 }
1215 #endif
1216 if (cur->children != NULL) {
1217 xmlNodePtr child = cur->children;
1218
1219 while (child != NULL) {
1220 ctxt->level = 0;
1221 #ifdef LIBXML_HTML_ENABLED
1222 if (is_xhtml)
1223 xhtmlNodeDumpOutput(ctxt, child);
1224 else
1225 #endif
1226 xmlNodeDumpOutputInternal(ctxt, child);
1227 xmlOutputBufferWrite(buf, 1, "\n");
1228 child = child->next;
1229 }
1230 }
1231 }
1232
1233 /*
1234 * Restore the state of the saving context at the end of the document
1235 */
1236 if ((switched_encoding) && (oldctxtenc == NULL)) {
1237 xmlSaveClearEncoding(ctxt);
1238 ctxt->escape = oldescape;
1239 ctxt->escapeAttr = oldescapeAttr;
1240 }
1241 cur->encoding = oldenc;
1242 return(0);
1243 }
1244
1245 #ifdef LIBXML_HTML_ENABLED
1246 /************************************************************************
1247 * *
1248 * Functions specific to XHTML serialization *
1249 * *
1250 ************************************************************************/
1251
1252 /**
1253 * xhtmlIsEmpty:
1254 * @node: the node
1255 *
1256 * Check if a node is an empty xhtml node
1257 *
1258 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1259 */
1260 static int
xhtmlIsEmpty(xmlNodePtr node)1261 xhtmlIsEmpty(xmlNodePtr node) {
1262 if (node == NULL)
1263 return(-1);
1264 if (node->type != XML_ELEMENT_NODE)
1265 return(0);
1266 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1267 return(0);
1268 if (node->children != NULL)
1269 return(0);
1270 switch (node->name[0]) {
1271 case 'a':
1272 if (xmlStrEqual(node->name, BAD_CAST "area"))
1273 return(1);
1274 return(0);
1275 case 'b':
1276 if (xmlStrEqual(node->name, BAD_CAST "br"))
1277 return(1);
1278 if (xmlStrEqual(node->name, BAD_CAST "base"))
1279 return(1);
1280 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1281 return(1);
1282 return(0);
1283 case 'c':
1284 if (xmlStrEqual(node->name, BAD_CAST "col"))
1285 return(1);
1286 return(0);
1287 case 'f':
1288 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1289 return(1);
1290 return(0);
1291 case 'h':
1292 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1293 return(1);
1294 return(0);
1295 case 'i':
1296 if (xmlStrEqual(node->name, BAD_CAST "img"))
1297 return(1);
1298 if (xmlStrEqual(node->name, BAD_CAST "input"))
1299 return(1);
1300 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1301 return(1);
1302 return(0);
1303 case 'l':
1304 if (xmlStrEqual(node->name, BAD_CAST "link"))
1305 return(1);
1306 return(0);
1307 case 'm':
1308 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1309 return(1);
1310 return(0);
1311 case 'p':
1312 if (xmlStrEqual(node->name, BAD_CAST "param"))
1313 return(1);
1314 return(0);
1315 }
1316 return(0);
1317 }
1318
1319 /**
1320 * xhtmlAttrListDumpOutput:
1321 * @cur: the first attribute pointer
1322 *
1323 * Dump a list of XML attributes
1324 */
1325 static void
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)1326 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1327 xmlAttrPtr xml_lang = NULL;
1328 xmlAttrPtr lang = NULL;
1329 xmlAttrPtr name = NULL;
1330 xmlAttrPtr id = NULL;
1331 xmlNodePtr parent;
1332 xmlOutputBufferPtr buf;
1333
1334 if (cur == NULL) return;
1335 buf = ctxt->buf;
1336 parent = cur->parent;
1337 while (cur != NULL) {
1338 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1339 id = cur;
1340 else
1341 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1342 name = cur;
1343 else
1344 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1345 lang = cur;
1346 else
1347 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1348 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1349 xml_lang = cur;
1350 else if ((cur->ns == NULL) &&
1351 ((cur->children == NULL) ||
1352 (cur->children->content == NULL) ||
1353 (cur->children->content[0] == 0)) &&
1354 (htmlIsBooleanAttr(cur->name))) {
1355 if (cur->children != NULL)
1356 xmlFreeNode(cur->children);
1357 cur->children = xmlNewText(cur->name);
1358 if (cur->children != NULL)
1359 cur->children->parent = (xmlNodePtr) cur;
1360 }
1361 xmlAttrDumpOutput(ctxt, cur);
1362 cur = cur->next;
1363 }
1364 /*
1365 * C.8
1366 */
1367 if ((name != NULL) && (id == NULL)) {
1368 if ((parent != NULL) && (parent->name != NULL) &&
1369 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1370 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1371 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1372 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1373 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1374 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1375 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1376 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1377 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1378 xmlOutputBufferWrite(buf, 5, " id=\"");
1379 xmlAttrSerializeContent(buf, name);
1380 xmlOutputBufferWrite(buf, 1, "\"");
1381 }
1382 }
1383 /*
1384 * C.7.
1385 */
1386 if ((lang != NULL) && (xml_lang == NULL)) {
1387 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1388 xmlAttrSerializeContent(buf, lang);
1389 xmlOutputBufferWrite(buf, 1, "\"");
1390 } else
1391 if ((xml_lang != NULL) && (lang == NULL)) {
1392 xmlOutputBufferWrite(buf, 7, " lang=\"");
1393 xmlAttrSerializeContent(buf, xml_lang);
1394 xmlOutputBufferWrite(buf, 1, "\"");
1395 }
1396 }
1397
1398 /**
1399 * xhtmlNodeListDumpOutput:
1400 * @buf: the XML buffer output
1401 * @doc: the XHTML document
1402 * @cur: the first node
1403 * @level: the imbrication level for indenting
1404 * @format: is formatting allowed
1405 * @encoding: an optional encoding string
1406 *
1407 * Dump an XML node list, recursive behaviour, children are printed too.
1408 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
1409 * or xmlKeepBlanksDefault(0) was called
1410 */
1411 static void
xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1412 xhtmlNodeListDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1413 xmlOutputBufferPtr buf;
1414
1415 if (cur == NULL) return;
1416 buf = ctxt->buf;
1417 while (cur != NULL) {
1418 if ((ctxt->format == 1) && (xmlIndentTreeOutput) &&
1419 (cur->type == XML_ELEMENT_NODE))
1420 xmlOutputBufferWrite(buf, ctxt->indent_size *
1421 (ctxt->level > ctxt->indent_nr ?
1422 ctxt->indent_nr : ctxt->level),
1423 ctxt->indent);
1424 xhtmlNodeDumpOutput(ctxt, cur);
1425 if (ctxt->format == 1) {
1426 xmlOutputBufferWrite(buf, 1, "\n");
1427 }
1428 cur = cur->next;
1429 }
1430 }
1431
1432 /**
1433 * xhtmlNodeDumpOutput:
1434 * @buf: the XML buffer output
1435 * @doc: the XHTML document
1436 * @cur: the current node
1437 * @level: the imbrication level for indenting
1438 * @format: is formatting allowed
1439 * @encoding: an optional encoding string
1440 *
1441 * Dump an XHTML node, recursive behaviour, children are printed too.
1442 */
1443 static void
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1444 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1445 int format, addmeta = 0;
1446 xmlNodePtr tmp;
1447 xmlChar *start, *end;
1448 xmlOutputBufferPtr buf;
1449
1450 if (cur == NULL) return;
1451 if ((cur->type == XML_DOCUMENT_NODE) ||
1452 (cur->type == XML_HTML_DOCUMENT_NODE)) {
1453 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1454 return;
1455 }
1456 if (cur->type == XML_XINCLUDE_START)
1457 return;
1458 if (cur->type == XML_XINCLUDE_END)
1459 return;
1460 if (cur->type == XML_NAMESPACE_DECL) {
1461 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1462 return;
1463 }
1464 if (cur->type == XML_DTD_NODE) {
1465 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1466 return;
1467 }
1468 if (cur->type == XML_DOCUMENT_FRAG_NODE) {
1469 xhtmlNodeListDumpOutput(ctxt, cur->children);
1470 return;
1471 }
1472 buf = ctxt->buf;
1473 if (cur->type == XML_ELEMENT_DECL) {
1474 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1475 return;
1476 }
1477 if (cur->type == XML_ATTRIBUTE_DECL) {
1478 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1479 return;
1480 }
1481 if (cur->type == XML_ENTITY_DECL) {
1482 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1483 return;
1484 }
1485 if (cur->type == XML_TEXT_NODE) {
1486 if (cur->content != NULL) {
1487 if ((cur->name == xmlStringText) ||
1488 (cur->name != xmlStringTextNoenc)) {
1489 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1490 } else {
1491 /*
1492 * Disable escaping, needed for XSLT
1493 */
1494 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1495 }
1496 }
1497
1498 return;
1499 }
1500 if (cur->type == XML_PI_NODE) {
1501 if (cur->content != NULL) {
1502 xmlOutputBufferWrite(buf, 2, "<?");
1503 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1504 if (cur->content != NULL) {
1505 xmlOutputBufferWrite(buf, 1, " ");
1506 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1507 }
1508 xmlOutputBufferWrite(buf, 2, "?>");
1509 } else {
1510 xmlOutputBufferWrite(buf, 2, "<?");
1511 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1512 xmlOutputBufferWrite(buf, 2, "?>");
1513 }
1514 return;
1515 }
1516 if (cur->type == XML_COMMENT_NODE) {
1517 if (cur->content != NULL) {
1518 xmlOutputBufferWrite(buf, 4, "<!--");
1519 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1520 xmlOutputBufferWrite(buf, 3, "-->");
1521 }
1522 return;
1523 }
1524 if (cur->type == XML_ENTITY_REF_NODE) {
1525 xmlOutputBufferWrite(buf, 1, "&");
1526 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1527 xmlOutputBufferWrite(buf, 1, ";");
1528 return;
1529 }
1530 if (cur->type == XML_CDATA_SECTION_NODE) {
1531 if (cur->content == NULL || *cur->content == '\0') {
1532 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1533 } else {
1534 start = end = cur->content;
1535 while (*end != '\0') {
1536 if (*end == ']' && *(end + 1) == ']' && *(end + 2) == '>') {
1537 end = end + 2;
1538 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1539 xmlOutputBufferWrite(buf, end - start, (const char *)start);
1540 xmlOutputBufferWrite(buf, 3, "]]>");
1541 start = end;
1542 }
1543 end++;
1544 }
1545 if (start != end) {
1546 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1547 xmlOutputBufferWriteString(buf, (const char *)start);
1548 xmlOutputBufferWrite(buf, 3, "]]>");
1549 }
1550 }
1551 return;
1552 }
1553 if (cur->type == XML_ATTRIBUTE_NODE) {
1554 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1555 return;
1556 }
1557
1558 format = ctxt->format;
1559 if (format == 1) {
1560 tmp = cur->children;
1561 while (tmp != NULL) {
1562 if ((tmp->type == XML_TEXT_NODE) ||
1563 (tmp->type == XML_ENTITY_REF_NODE)) {
1564 format = 0;
1565 break;
1566 }
1567 tmp = tmp->next;
1568 }
1569 }
1570 xmlOutputBufferWrite(buf, 1, "<");
1571 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1572 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1573 xmlOutputBufferWrite(buf, 1, ":");
1574 }
1575
1576 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1577 if (cur->nsDef)
1578 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1579 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1580 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1581 /*
1582 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1583 */
1584 xmlOutputBufferWriteString(buf,
1585 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1586 }
1587 if (cur->properties != NULL)
1588 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1589
1590 if ((cur->type == XML_ELEMENT_NODE) &&
1591 (cur->parent != NULL) &&
1592 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1593 xmlStrEqual(cur->name, BAD_CAST"head") &&
1594 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1595
1596 tmp = cur->children;
1597 while (tmp != NULL) {
1598 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1599 xmlChar *httpequiv;
1600
1601 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1602 if (httpequiv != NULL) {
1603 if (xmlStrcasecmp(httpequiv, BAD_CAST"Content-Type") == 0) {
1604 xmlFree(httpequiv);
1605 break;
1606 }
1607 xmlFree(httpequiv);
1608 }
1609 }
1610 tmp = tmp->next;
1611 }
1612 if (tmp == NULL)
1613 addmeta = 1;
1614 }
1615
1616 if ((cur->type == XML_ELEMENT_NODE) && (cur->children == NULL)) {
1617 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1618 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1619 /*
1620 * C.2. Empty Elements
1621 */
1622 xmlOutputBufferWrite(buf, 3, " />");
1623 } else {
1624 if (addmeta == 1) {
1625 xmlOutputBufferWrite(buf, 1, ">");
1626 if (ctxt->format == 1) {
1627 xmlOutputBufferWrite(buf, 1, "\n");
1628 if (xmlIndentTreeOutput)
1629 xmlOutputBufferWrite(buf, ctxt->indent_size *
1630 (ctxt->level + 1 > ctxt->indent_nr ?
1631 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1632 }
1633 xmlOutputBufferWriteString(buf,
1634 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1635 if (ctxt->encoding) {
1636 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1637 } else {
1638 xmlOutputBufferWrite(buf, 5, "UTF-8");
1639 }
1640 xmlOutputBufferWrite(buf, 4, "\" />");
1641 if (ctxt->format == 1)
1642 xmlOutputBufferWrite(buf, 1, "\n");
1643 } else {
1644 xmlOutputBufferWrite(buf, 1, ">");
1645 }
1646 /*
1647 * C.3. Element Minimization and Empty Element Content
1648 */
1649 xmlOutputBufferWrite(buf, 2, "</");
1650 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1651 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1652 xmlOutputBufferWrite(buf, 1, ":");
1653 }
1654 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1655 xmlOutputBufferWrite(buf, 1, ">");
1656 }
1657 return;
1658 }
1659 xmlOutputBufferWrite(buf, 1, ">");
1660 if (addmeta == 1) {
1661 if (ctxt->format == 1) {
1662 xmlOutputBufferWrite(buf, 1, "\n");
1663 if (xmlIndentTreeOutput)
1664 xmlOutputBufferWrite(buf, ctxt->indent_size *
1665 (ctxt->level + 1 > ctxt->indent_nr ?
1666 ctxt->indent_nr : ctxt->level + 1), ctxt->indent);
1667 }
1668 xmlOutputBufferWriteString(buf,
1669 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
1670 if (ctxt->encoding) {
1671 xmlOutputBufferWriteString(buf, (const char *)ctxt->encoding);
1672 } else {
1673 xmlOutputBufferWrite(buf, 5, "UTF-8");
1674 }
1675 xmlOutputBufferWrite(buf, 4, "\" />");
1676 }
1677 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) {
1678 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1679 }
1680
1681 #if 0
1682 /*
1683 * This was removed due to problems with HTML processors.
1684 * See bug #345147.
1685 */
1686 /*
1687 * 4.8. Script and Style elements
1688 */
1689 if ((cur->type == XML_ELEMENT_NODE) &&
1690 ((xmlStrEqual(cur->name, BAD_CAST "script")) ||
1691 (xmlStrEqual(cur->name, BAD_CAST "style"))) &&
1692 ((cur->ns == NULL) ||
1693 (xmlStrEqual(cur->ns->href, XHTML_NS_NAME)))) {
1694 xmlNodePtr child = cur->children;
1695
1696 while (child != NULL) {
1697 if (child->type == XML_TEXT_NODE) {
1698 if ((xmlStrchr(child->content, '<') == NULL) &&
1699 (xmlStrchr(child->content, '&') == NULL) &&
1700 (xmlStrstr(child->content, BAD_CAST "]]>") == NULL)) {
1701 /* Nothing to escape, so just output as is... */
1702 /* FIXME: Should we do something about "--" also? */
1703 int level = ctxt->level;
1704 int indent = ctxt->format;
1705
1706 ctxt->level = 0;
1707 ctxt->format = 0;
1708 xmlOutputBufferWriteString(buf, (const char *) child->content);
1709 /* (We cannot use xhtmlNodeDumpOutput() here because
1710 * we wish to leave '>' unescaped!) */
1711 ctxt->level = level;
1712 ctxt->format = indent;
1713 } else {
1714 /* We must use a CDATA section. Unfortunately,
1715 * this will break CSS and JavaScript when read by
1716 * a browser in HTML4-compliant mode. :-( */
1717 start = end = child->content;
1718 while (*end != '\0') {
1719 if (*end == ']' &&
1720 *(end + 1) == ']' &&
1721 *(end + 2) == '>') {
1722 end = end + 2;
1723 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1724 xmlOutputBufferWrite(buf, end - start,
1725 (const char *)start);
1726 xmlOutputBufferWrite(buf, 3, "]]>");
1727 start = end;
1728 }
1729 end++;
1730 }
1731 if (start != end) {
1732 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1733 xmlOutputBufferWrite(buf, end - start,
1734 (const char *)start);
1735 xmlOutputBufferWrite(buf, 3, "]]>");
1736 }
1737 }
1738 } else {
1739 int level = ctxt->level;
1740 int indent = ctxt->format;
1741
1742 ctxt->level = 0;
1743 ctxt->format = 0;
1744 xhtmlNodeDumpOutput(ctxt, child);
1745 ctxt->level = level;
1746 ctxt->format = indent;
1747 }
1748 child = child->next;
1749 }
1750 }
1751 #endif
1752
1753 if (cur->children != NULL) {
1754 int indent = ctxt->format;
1755
1756 if (format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1757 if (ctxt->level >= 0) ctxt->level++;
1758 ctxt->format = format;
1759 xhtmlNodeListDumpOutput(ctxt, cur->children);
1760 if (ctxt->level > 0) ctxt->level--;
1761 ctxt->format = indent;
1762 if ((xmlIndentTreeOutput) && (format == 1))
1763 xmlOutputBufferWrite(buf, ctxt->indent_size *
1764 (ctxt->level > ctxt->indent_nr ?
1765 ctxt->indent_nr : ctxt->level),
1766 ctxt->indent);
1767 }
1768 xmlOutputBufferWrite(buf, 2, "</");
1769 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1770 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1771 xmlOutputBufferWrite(buf, 1, ":");
1772 }
1773
1774 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1775 xmlOutputBufferWrite(buf, 1, ">");
1776 }
1777 #endif
1778
1779 /************************************************************************
1780 * *
1781 * Public entry points *
1782 * *
1783 ************************************************************************/
1784
1785 /**
1786 * xmlSaveToFd:
1787 * @fd: a file descriptor number
1788 * @encoding: the encoding name to use or NULL
1789 * @options: a set of xmlSaveOptions
1790 *
1791 * Create a document saving context serializing to a file descriptor
1792 * with the encoding and the options given.
1793 *
1794 * Returns a new serialization context or NULL in case of error.
1795 */
1796 xmlSaveCtxtPtr
xmlSaveToFd(int fd,const char * encoding,int options)1797 xmlSaveToFd(int fd, const char *encoding, int options)
1798 {
1799 xmlSaveCtxtPtr ret;
1800
1801 ret = xmlNewSaveCtxt(encoding, options);
1802 if (ret == NULL) return(NULL);
1803 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1804 if (ret->buf == NULL) {
1805 xmlFreeSaveCtxt(ret);
1806 return(NULL);
1807 }
1808 return(ret);
1809 }
1810
1811 /**
1812 * xmlSaveToFilename:
1813 * @filename: a file name or an URL
1814 * @encoding: the encoding name to use or NULL
1815 * @options: a set of xmlSaveOptions
1816 *
1817 * Create a document saving context serializing to a filename or possibly
1818 * to an URL (but this is less reliable) with the encoding and the options
1819 * given.
1820 *
1821 * Returns a new serialization context or NULL in case of error.
1822 */
1823 xmlSaveCtxtPtr
xmlSaveToFilename(const char * filename,const char * encoding,int options)1824 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1825 {
1826 xmlSaveCtxtPtr ret;
1827 int compression = 0; /* TODO handle compression option */
1828
1829 ret = xmlNewSaveCtxt(encoding, options);
1830 if (ret == NULL) return(NULL);
1831 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1832 compression);
1833 if (ret->buf == NULL) {
1834 xmlFreeSaveCtxt(ret);
1835 return(NULL);
1836 }
1837 return(ret);
1838 }
1839
1840 /**
1841 * xmlSaveToBuffer:
1842 * @buffer: a buffer
1843 * @encoding: the encoding name to use or NULL
1844 * @options: a set of xmlSaveOptions
1845 *
1846 * Create a document saving context serializing to a buffer
1847 * with the encoding and the options given
1848 *
1849 * Returns a new serialization context or NULL in case of error.
1850 */
1851
1852 xmlSaveCtxtPtr
xmlSaveToBuffer(xmlBufferPtr buffer,const char * encoding,int options)1853 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1854 {
1855 xmlSaveCtxtPtr ret;
1856 xmlOutputBufferPtr out_buff;
1857 xmlCharEncodingHandlerPtr handler;
1858
1859 ret = xmlNewSaveCtxt(encoding, options);
1860 if (ret == NULL) return(NULL);
1861
1862 if (encoding != NULL) {
1863 handler = xmlFindCharEncodingHandler(encoding);
1864 if (handler == NULL) {
1865 xmlFree(ret);
1866 return(NULL);
1867 }
1868 } else
1869 handler = NULL;
1870 out_buff = xmlOutputBufferCreateBuffer(buffer, handler);
1871 if (out_buff == NULL) {
1872 xmlFree(ret);
1873 if (handler) xmlCharEncCloseFunc(handler);
1874 return(NULL);
1875 }
1876
1877 ret->buf = out_buff;
1878 return(ret);
1879 }
1880
1881 /**
1882 * xmlSaveToIO:
1883 * @iowrite: an I/O write function
1884 * @ioclose: an I/O close function
1885 * @ioctx: an I/O handler
1886 * @encoding: the encoding name to use or NULL
1887 * @options: a set of xmlSaveOptions
1888 *
1889 * Create a document saving context serializing to a file descriptor
1890 * with the encoding and the options given
1891 *
1892 * Returns a new serialization context or NULL in case of error.
1893 */
1894 xmlSaveCtxtPtr
xmlSaveToIO(xmlOutputWriteCallback iowrite,xmlOutputCloseCallback ioclose,void * ioctx,const char * encoding,int options)1895 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1896 xmlOutputCloseCallback ioclose,
1897 void *ioctx, const char *encoding, int options)
1898 {
1899 xmlSaveCtxtPtr ret;
1900
1901 ret = xmlNewSaveCtxt(encoding, options);
1902 if (ret == NULL) return(NULL);
1903 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1904 if (ret->buf == NULL) {
1905 xmlFreeSaveCtxt(ret);
1906 return(NULL);
1907 }
1908 return(ret);
1909 }
1910
1911 /**
1912 * xmlSaveDoc:
1913 * @ctxt: a document saving context
1914 * @doc: a document
1915 *
1916 * Save a full document to a saving context
1917 * TODO: The function is not fully implemented yet as it does not return the
1918 * byte count but 0 instead
1919 *
1920 * Returns the number of byte written or -1 in case of error
1921 */
1922 long
xmlSaveDoc(xmlSaveCtxtPtr ctxt,xmlDocPtr doc)1923 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1924 {
1925 long ret = 0;
1926
1927 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1928 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1929 return(-1);
1930 return(ret);
1931 }
1932
1933 /**
1934 * xmlSaveTree:
1935 * @ctxt: a document saving context
1936 * @node: the top node of the subtree to save
1937 *
1938 * Save a subtree starting at the node parameter to a saving context
1939 * TODO: The function is not fully implemented yet as it does not return the
1940 * byte count but 0 instead
1941 *
1942 * Returns the number of byte written or -1 in case of error
1943 */
1944 long
xmlSaveTree(xmlSaveCtxtPtr ctxt,xmlNodePtr node)1945 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr node)
1946 {
1947 long ret = 0;
1948
1949 if ((ctxt == NULL) || (node == NULL)) return(-1);
1950 xmlNodeDumpOutputInternal(ctxt, node);
1951 return(ret);
1952 }
1953
1954 /**
1955 * xmlSaveFlush:
1956 * @ctxt: a document saving context
1957 *
1958 * Flush a document saving context, i.e. make sure that all bytes have
1959 * been output.
1960 *
1961 * Returns the number of byte written or -1 in case of error.
1962 */
1963 int
xmlSaveFlush(xmlSaveCtxtPtr ctxt)1964 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1965 {
1966 if (ctxt == NULL) return(-1);
1967 if (ctxt->buf == NULL) return(-1);
1968 return(xmlOutputBufferFlush(ctxt->buf));
1969 }
1970
1971 /**
1972 * xmlSaveClose:
1973 * @ctxt: a document saving context
1974 *
1975 * Close a document saving context, i.e. make sure that all bytes have
1976 * been output and free the associated data.
1977 *
1978 * Returns the number of byte written or -1 in case of error.
1979 */
1980 int
xmlSaveClose(xmlSaveCtxtPtr ctxt)1981 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1982 {
1983 int ret;
1984
1985 if (ctxt == NULL) return(-1);
1986 ret = xmlSaveFlush(ctxt);
1987 xmlFreeSaveCtxt(ctxt);
1988 return(ret);
1989 }
1990
1991 /**
1992 * xmlSaveSetEscape:
1993 * @ctxt: a document saving context
1994 * @escape: the escaping function
1995 *
1996 * Set a custom escaping function to be used for text in element content
1997 *
1998 * Returns 0 if successful or -1 in case of error.
1999 */
2000 int
xmlSaveSetEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)2001 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
2002 {
2003 if (ctxt == NULL) return(-1);
2004 ctxt->escape = escape;
2005 return(0);
2006 }
2007
2008 /**
2009 * xmlSaveSetAttrEscape:
2010 * @ctxt: a document saving context
2011 * @escape: the escaping function
2012 *
2013 * Set a custom escaping function to be used for text in attribute content
2014 *
2015 * Returns 0 if successful or -1 in case of error.
2016 */
2017 int
xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)2018 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
2019 {
2020 if (ctxt == NULL) return(-1);
2021 ctxt->escapeAttr = escape;
2022 return(0);
2023 }
2024
2025 /************************************************************************
2026 * *
2027 * Public entry points based on buffers *
2028 * *
2029 ************************************************************************/
2030
2031 /**
2032 * xmlBufAttrSerializeTxtContent:
2033 * @buf: and xmlBufPtr output
2034 * @doc: the document
2035 * @attr: the attribute node
2036 * @string: the text content
2037 *
2038 * Serialize text attribute values to an xmlBufPtr
2039 */
2040 void
xmlBufAttrSerializeTxtContent(xmlBufPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2041 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
2042 xmlAttrPtr attr, const xmlChar * string)
2043 {
2044 xmlChar *base, *cur;
2045
2046 if (string == NULL)
2047 return;
2048 base = cur = (xmlChar *) string;
2049 while (*cur != 0) {
2050 if (*cur == '\n') {
2051 if (base != cur)
2052 xmlBufAdd(buf, base, cur - base);
2053 xmlBufAdd(buf, BAD_CAST " ", 5);
2054 cur++;
2055 base = cur;
2056 } else if (*cur == '\r') {
2057 if (base != cur)
2058 xmlBufAdd(buf, base, cur - base);
2059 xmlBufAdd(buf, BAD_CAST " ", 5);
2060 cur++;
2061 base = cur;
2062 } else if (*cur == '\t') {
2063 if (base != cur)
2064 xmlBufAdd(buf, base, cur - base);
2065 xmlBufAdd(buf, BAD_CAST "	", 4);
2066 cur++;
2067 base = cur;
2068 } else if (*cur == '"') {
2069 if (base != cur)
2070 xmlBufAdd(buf, base, cur - base);
2071 xmlBufAdd(buf, BAD_CAST """, 6);
2072 cur++;
2073 base = cur;
2074 } else if (*cur == '<') {
2075 if (base != cur)
2076 xmlBufAdd(buf, base, cur - base);
2077 xmlBufAdd(buf, BAD_CAST "<", 4);
2078 cur++;
2079 base = cur;
2080 } else if (*cur == '>') {
2081 if (base != cur)
2082 xmlBufAdd(buf, base, cur - base);
2083 xmlBufAdd(buf, BAD_CAST ">", 4);
2084 cur++;
2085 base = cur;
2086 } else if (*cur == '&') {
2087 if (base != cur)
2088 xmlBufAdd(buf, base, cur - base);
2089 xmlBufAdd(buf, BAD_CAST "&", 5);
2090 cur++;
2091 base = cur;
2092 } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2093 ((doc == NULL) || (doc->encoding == NULL))) {
2094 /*
2095 * We assume we have UTF-8 content.
2096 */
2097 unsigned char tmp[12];
2098 int val = 0, l = 1;
2099
2100 if (base != cur)
2101 xmlBufAdd(buf, base, cur - base);
2102 if (*cur < 0xC0) {
2103 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2104 xmlSerializeHexCharRef(tmp, *cur);
2105 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2106 cur++;
2107 base = cur;
2108 continue;
2109 } else if (*cur < 0xE0) {
2110 val = (cur[0]) & 0x1F;
2111 val <<= 6;
2112 val |= (cur[1]) & 0x3F;
2113 l = 2;
2114 } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2115 val = (cur[0]) & 0x0F;
2116 val <<= 6;
2117 val |= (cur[1]) & 0x3F;
2118 val <<= 6;
2119 val |= (cur[2]) & 0x3F;
2120 l = 3;
2121 } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2122 val = (cur[0]) & 0x07;
2123 val <<= 6;
2124 val |= (cur[1]) & 0x3F;
2125 val <<= 6;
2126 val |= (cur[2]) & 0x3F;
2127 val <<= 6;
2128 val |= (cur[3]) & 0x3F;
2129 l = 4;
2130 }
2131 if ((l == 1) || (!IS_CHAR(val))) {
2132 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2133 xmlSerializeHexCharRef(tmp, *cur);
2134 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2135 cur++;
2136 base = cur;
2137 continue;
2138 }
2139 /*
2140 * We could do multiple things here. Just save
2141 * as a char ref
2142 */
2143 xmlSerializeHexCharRef(tmp, val);
2144 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2145 cur += l;
2146 base = cur;
2147 } else {
2148 cur++;
2149 }
2150 }
2151 if (base != cur)
2152 xmlBufAdd(buf, base, cur - base);
2153 }
2154
2155 /**
2156 * xmlAttrSerializeTxtContent:
2157 * @buf: the XML buffer output
2158 * @doc: the document
2159 * @attr: the attribute node
2160 * @string: the text content
2161 *
2162 * Serialize text attribute values to an xml simple buffer
2163 */
2164 void
xmlAttrSerializeTxtContent(xmlBufferPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2165 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2166 xmlAttrPtr attr, const xmlChar * string)
2167 {
2168 xmlBufPtr buffer;
2169
2170 if ((buf == NULL) || (string == NULL))
2171 return;
2172 buffer = xmlBufFromBuffer(buf);
2173 if (buffer == NULL)
2174 return;
2175 xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2176 xmlBufBackToBuffer(buffer);
2177 }
2178
2179 /**
2180 * xmlNodeDump:
2181 * @buf: the XML buffer output
2182 * @doc: the document
2183 * @cur: the current node
2184 * @level: the imbrication level for indenting
2185 * @format: is formatting allowed
2186 *
2187 * Dump an XML node, recursive behaviour,children are printed too.
2188 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2189 * or xmlKeepBlanksDefault(0) was called.
2190 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2191 * deprecated, use xmlNodeDumpOutput() instead.
2192 *
2193 * Returns the number of bytes written to the buffer or -1 in case of error
2194 */
2195 int
xmlNodeDump(xmlBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2196 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2197 int format)
2198 {
2199 xmlBufPtr buffer;
2200 int ret;
2201
2202 if ((buf == NULL) || (cur == NULL))
2203 return(-1);
2204 buffer = xmlBufFromBuffer(buf);
2205 if (buffer == NULL)
2206 return(-1);
2207 ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2208 xmlBufBackToBuffer(buffer);
2209 if (ret > INT_MAX)
2210 return(-1);
2211 return((int) ret);
2212 }
2213
2214 /**
2215 * xmlBufNodeDump:
2216 * @buf: the XML buffer output
2217 * @doc: the document
2218 * @cur: the current node
2219 * @level: the imbrication level for indenting
2220 * @format: is formatting allowed
2221 *
2222 * Dump an XML node, recursive behaviour,children are printed too.
2223 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2224 * or xmlKeepBlanksDefault(0) was called
2225 *
2226 * Returns the number of bytes written to the buffer, in case of error 0
2227 * is returned or @buf stores the error
2228 */
2229
2230 size_t
xmlBufNodeDump(xmlBufPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2231 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2232 int format)
2233 {
2234 size_t use;
2235 int ret;
2236 xmlOutputBufferPtr outbuf;
2237 int oldalloc;
2238
2239 xmlInitParser();
2240
2241 if (cur == NULL) {
2242 #ifdef DEBUG_TREE
2243 xmlGenericError(xmlGenericErrorContext,
2244 "xmlNodeDump : node == NULL\n");
2245 #endif
2246 return (-1);
2247 }
2248 if (buf == NULL) {
2249 #ifdef DEBUG_TREE
2250 xmlGenericError(xmlGenericErrorContext,
2251 "xmlNodeDump : buf == NULL\n");
2252 #endif
2253 return (-1);
2254 }
2255 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2256 if (outbuf == NULL) {
2257 xmlSaveErrMemory("creating buffer");
2258 return (-1);
2259 }
2260 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2261 outbuf->buffer = buf;
2262 outbuf->encoder = NULL;
2263 outbuf->writecallback = NULL;
2264 outbuf->closecallback = NULL;
2265 outbuf->context = NULL;
2266 outbuf->written = 0;
2267
2268 use = xmlBufUse(buf);
2269 oldalloc = xmlBufGetAllocationScheme(buf);
2270 xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2271 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2272 xmlBufSetAllocationScheme(buf, oldalloc);
2273 xmlFree(outbuf);
2274 ret = xmlBufUse(buf) - use;
2275 return (ret);
2276 }
2277
2278 /**
2279 * xmlElemDump:
2280 * @f: the FILE * for the output
2281 * @doc: the document
2282 * @cur: the current node
2283 *
2284 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2285 */
2286 void
xmlElemDump(FILE * f,xmlDocPtr doc,xmlNodePtr cur)2287 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2288 {
2289 xmlOutputBufferPtr outbuf;
2290
2291 xmlInitParser();
2292
2293 if (cur == NULL) {
2294 #ifdef DEBUG_TREE
2295 xmlGenericError(xmlGenericErrorContext,
2296 "xmlElemDump : cur == NULL\n");
2297 #endif
2298 return;
2299 }
2300 #ifdef DEBUG_TREE
2301 if (doc == NULL) {
2302 xmlGenericError(xmlGenericErrorContext,
2303 "xmlElemDump : doc == NULL\n");
2304 }
2305 #endif
2306
2307 outbuf = xmlOutputBufferCreateFile(f, NULL);
2308 if (outbuf == NULL)
2309 return;
2310 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2311 #ifdef LIBXML_HTML_ENABLED
2312 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2313 #else
2314 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2315 #endif /* LIBXML_HTML_ENABLED */
2316 } else
2317 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2318 xmlOutputBufferClose(outbuf);
2319 }
2320
2321 /************************************************************************
2322 * *
2323 * Saving functions front-ends *
2324 * *
2325 ************************************************************************/
2326
2327 /**
2328 * xmlNodeDumpOutput:
2329 * @buf: the XML buffer output
2330 * @doc: the document
2331 * @cur: the current node
2332 * @level: the imbrication level for indenting
2333 * @format: is formatting allowed
2334 * @encoding: an optional encoding string
2335 *
2336 * Dump an XML node, recursive behaviour, children are printed too.
2337 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2338 * or xmlKeepBlanksDefault(0) was called
2339 */
2340 void
xmlNodeDumpOutput(xmlOutputBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format,const char * encoding)2341 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2342 int level, int format, const char *encoding)
2343 {
2344 xmlSaveCtxt ctxt;
2345 #ifdef LIBXML_HTML_ENABLED
2346 xmlDtdPtr dtd;
2347 int is_xhtml = 0;
2348 #endif
2349
2350 xmlInitParser();
2351
2352 if ((buf == NULL) || (cur == NULL)) return;
2353
2354 if (encoding == NULL)
2355 encoding = "UTF-8";
2356
2357 memset(&ctxt, 0, sizeof(ctxt));
2358 ctxt.buf = buf;
2359 ctxt.level = level;
2360 ctxt.format = format ? 1 : 0;
2361 ctxt.encoding = (const xmlChar *) encoding;
2362 xmlSaveCtxtInit(&ctxt);
2363 ctxt.options |= XML_SAVE_AS_XML;
2364
2365 #ifdef LIBXML_HTML_ENABLED
2366 dtd = xmlGetIntSubset(doc);
2367 if (dtd != NULL) {
2368 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2369 if (is_xhtml < 0)
2370 is_xhtml = 0;
2371 }
2372
2373 if (is_xhtml)
2374 xhtmlNodeDumpOutput(&ctxt, cur);
2375 else
2376 #endif
2377 xmlNodeDumpOutputInternal(&ctxt, cur);
2378 }
2379
2380 /**
2381 * xmlDocDumpFormatMemoryEnc:
2382 * @out_doc: Document to generate XML text from
2383 * @doc_txt_ptr: Memory pointer for allocated XML text
2384 * @doc_txt_len: Length of the generated XML text
2385 * @txt_encoding: Character encoding to use when generating XML text
2386 * @format: should formatting spaces been added
2387 *
2388 * Dump the current DOM tree into memory using the character encoding specified
2389 * by the caller. Note it is up to the caller of this function to free the
2390 * allocated memory with xmlFree().
2391 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2392 * or xmlKeepBlanksDefault(0) was called
2393 */
2394
2395 void
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding,int format)2396 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2397 int * doc_txt_len, const char * txt_encoding,
2398 int format) {
2399 xmlSaveCtxt ctxt;
2400 int dummy = 0;
2401 xmlOutputBufferPtr out_buff = NULL;
2402 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2403
2404 if (doc_txt_len == NULL) {
2405 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2406 }
2407
2408 if (doc_txt_ptr == NULL) {
2409 *doc_txt_len = 0;
2410 return;
2411 }
2412
2413 *doc_txt_ptr = NULL;
2414 *doc_txt_len = 0;
2415
2416 if (out_doc == NULL) {
2417 /* No document, no output */
2418 return;
2419 }
2420
2421 /*
2422 * Validate the encoding value, if provided.
2423 * This logic is copied from xmlSaveFileEnc.
2424 */
2425
2426 if (txt_encoding == NULL)
2427 txt_encoding = (const char *) out_doc->encoding;
2428 if (txt_encoding != NULL) {
2429 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2430 if ( conv_hdlr == NULL ) {
2431 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2432 txt_encoding);
2433 return;
2434 }
2435 }
2436
2437 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2438 xmlSaveErrMemory("creating buffer");
2439 return;
2440 }
2441
2442 memset(&ctxt, 0, sizeof(ctxt));
2443 ctxt.buf = out_buff;
2444 ctxt.level = 0;
2445 ctxt.format = format ? 1 : 0;
2446 ctxt.encoding = (const xmlChar *) txt_encoding;
2447 xmlSaveCtxtInit(&ctxt);
2448 ctxt.options |= XML_SAVE_AS_XML;
2449 xmlDocContentDumpOutput(&ctxt, out_doc);
2450 xmlOutputBufferFlush(out_buff);
2451 if (out_buff->conv != NULL) {
2452 *doc_txt_len = xmlBufUse(out_buff->conv);
2453 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2454 } else {
2455 *doc_txt_len = xmlBufUse(out_buff->buffer);
2456 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2457 }
2458 (void)xmlOutputBufferClose(out_buff);
2459
2460 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2461 *doc_txt_len = 0;
2462 xmlSaveErrMemory("creating output");
2463 }
2464
2465 return;
2466 }
2467
2468 /**
2469 * xmlDocDumpMemory:
2470 * @cur: the document
2471 * @mem: OUT: the memory pointer
2472 * @size: OUT: the memory length
2473 *
2474 * Dump an XML document in memory and return the #xmlChar * and it's size
2475 * in bytes. It's up to the caller to free the memory with xmlFree().
2476 * The resulting byte array is zero terminated, though the last 0 is not
2477 * included in the returned size.
2478 */
2479 void
xmlDocDumpMemory(xmlDocPtr cur,xmlChar ** mem,int * size)2480 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2481 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2482 }
2483
2484 /**
2485 * xmlDocDumpFormatMemory:
2486 * @cur: the document
2487 * @mem: OUT: the memory pointer
2488 * @size: OUT: the memory length
2489 * @format: should formatting spaces been added
2490 *
2491 *
2492 * Dump an XML document in memory and return the #xmlChar * and it's size.
2493 * It's up to the caller to free the memory with xmlFree().
2494 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2495 * or xmlKeepBlanksDefault(0) was called
2496 */
2497 void
xmlDocDumpFormatMemory(xmlDocPtr cur,xmlChar ** mem,int * size,int format)2498 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2499 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2500 }
2501
2502 /**
2503 * xmlDocDumpMemoryEnc:
2504 * @out_doc: Document to generate XML text from
2505 * @doc_txt_ptr: Memory pointer for allocated XML text
2506 * @doc_txt_len: Length of the generated XML text
2507 * @txt_encoding: Character encoding to use when generating XML text
2508 *
2509 * Dump the current DOM tree into memory using the character encoding specified
2510 * by the caller. Note it is up to the caller of this function to free the
2511 * allocated memory with xmlFree().
2512 */
2513
2514 void
xmlDocDumpMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding)2515 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2516 int * doc_txt_len, const char * txt_encoding) {
2517 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2518 txt_encoding, 0);
2519 }
2520
2521 /**
2522 * xmlDocFormatDump:
2523 * @f: the FILE*
2524 * @cur: the document
2525 * @format: should formatting spaces been added
2526 *
2527 * Dump an XML document to an open FILE.
2528 *
2529 * returns: the number of bytes written or -1 in case of failure.
2530 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2531 * or xmlKeepBlanksDefault(0) was called
2532 */
2533 int
xmlDocFormatDump(FILE * f,xmlDocPtr cur,int format)2534 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2535 xmlSaveCtxt ctxt;
2536 xmlOutputBufferPtr buf;
2537 const char * encoding;
2538 xmlCharEncodingHandlerPtr handler = NULL;
2539 int ret;
2540
2541 if (cur == NULL) {
2542 #ifdef DEBUG_TREE
2543 xmlGenericError(xmlGenericErrorContext,
2544 "xmlDocDump : document == NULL\n");
2545 #endif
2546 return(-1);
2547 }
2548 encoding = (const char *) cur->encoding;
2549
2550 if (encoding != NULL) {
2551 handler = xmlFindCharEncodingHandler(encoding);
2552 if (handler == NULL) {
2553 xmlFree((char *) cur->encoding);
2554 cur->encoding = NULL;
2555 encoding = NULL;
2556 }
2557 }
2558 buf = xmlOutputBufferCreateFile(f, handler);
2559 if (buf == NULL) return(-1);
2560 memset(&ctxt, 0, sizeof(ctxt));
2561 ctxt.buf = buf;
2562 ctxt.level = 0;
2563 ctxt.format = format ? 1 : 0;
2564 ctxt.encoding = (const xmlChar *) encoding;
2565 xmlSaveCtxtInit(&ctxt);
2566 ctxt.options |= XML_SAVE_AS_XML;
2567 xmlDocContentDumpOutput(&ctxt, cur);
2568
2569 ret = xmlOutputBufferClose(buf);
2570 return(ret);
2571 }
2572
2573 /**
2574 * xmlDocDump:
2575 * @f: the FILE*
2576 * @cur: the document
2577 *
2578 * Dump an XML document to an open FILE.
2579 *
2580 * returns: the number of bytes written or -1 in case of failure.
2581 */
2582 int
xmlDocDump(FILE * f,xmlDocPtr cur)2583 xmlDocDump(FILE *f, xmlDocPtr cur) {
2584 return(xmlDocFormatDump (f, cur, 0));
2585 }
2586
2587 /**
2588 * xmlSaveFileTo:
2589 * @buf: an output I/O buffer
2590 * @cur: the document
2591 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2592 *
2593 * Dump an XML document to an I/O buffer.
2594 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2595 * after this call.
2596 *
2597 * returns: the number of bytes written or -1 in case of failure.
2598 */
2599 int
xmlSaveFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding)2600 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2601 xmlSaveCtxt ctxt;
2602 int ret;
2603
2604 if (buf == NULL) return(-1);
2605 if (cur == NULL) {
2606 xmlOutputBufferClose(buf);
2607 return(-1);
2608 }
2609 memset(&ctxt, 0, sizeof(ctxt));
2610 ctxt.buf = buf;
2611 ctxt.level = 0;
2612 ctxt.format = 0;
2613 ctxt.encoding = (const xmlChar *) encoding;
2614 xmlSaveCtxtInit(&ctxt);
2615 ctxt.options |= XML_SAVE_AS_XML;
2616 xmlDocContentDumpOutput(&ctxt, cur);
2617 ret = xmlOutputBufferClose(buf);
2618 return(ret);
2619 }
2620
2621 /**
2622 * xmlSaveFormatFileTo:
2623 * @buf: an output I/O buffer
2624 * @cur: the document
2625 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2626 * @format: should formatting spaces been added
2627 *
2628 * Dump an XML document to an I/O buffer.
2629 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2630 * after this call.
2631 *
2632 * returns: the number of bytes written or -1 in case of failure.
2633 */
2634 int
xmlSaveFormatFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding,int format)2635 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2636 const char *encoding, int format)
2637 {
2638 xmlSaveCtxt ctxt;
2639 int ret;
2640
2641 if (buf == NULL) return(-1);
2642 if ((cur == NULL) ||
2643 ((cur->type != XML_DOCUMENT_NODE) &&
2644 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2645 xmlOutputBufferClose(buf);
2646 return(-1);
2647 }
2648 memset(&ctxt, 0, sizeof(ctxt));
2649 ctxt.buf = buf;
2650 ctxt.level = 0;
2651 ctxt.format = format ? 1 : 0;
2652 ctxt.encoding = (const xmlChar *) encoding;
2653 xmlSaveCtxtInit(&ctxt);
2654 ctxt.options |= XML_SAVE_AS_XML;
2655 xmlDocContentDumpOutput(&ctxt, cur);
2656 ret = xmlOutputBufferClose(buf);
2657 return (ret);
2658 }
2659
2660 /**
2661 * xmlSaveFormatFileEnc:
2662 * @filename: the filename or URL to output
2663 * @cur: the document being saved
2664 * @encoding: the name of the encoding to use or NULL.
2665 * @format: should formatting spaces be added.
2666 *
2667 * Dump an XML document to a file or an URL.
2668 *
2669 * Returns the number of bytes written or -1 in case of error.
2670 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2671 * or xmlKeepBlanksDefault(0) was called
2672 */
2673 int
xmlSaveFormatFileEnc(const char * filename,xmlDocPtr cur,const char * encoding,int format)2674 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2675 const char * encoding, int format ) {
2676 xmlSaveCtxt ctxt;
2677 xmlOutputBufferPtr buf;
2678 xmlCharEncodingHandlerPtr handler = NULL;
2679 int ret;
2680
2681 if (cur == NULL)
2682 return(-1);
2683
2684 if (encoding == NULL)
2685 encoding = (const char *) cur->encoding;
2686
2687 if (encoding != NULL) {
2688
2689 handler = xmlFindCharEncodingHandler(encoding);
2690 if (handler == NULL)
2691 return(-1);
2692 }
2693
2694 #ifdef LIBXML_ZLIB_ENABLED
2695 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2696 #endif
2697 /*
2698 * save the content to a temp buffer.
2699 */
2700 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2701 if (buf == NULL) return(-1);
2702 memset(&ctxt, 0, sizeof(ctxt));
2703 ctxt.buf = buf;
2704 ctxt.level = 0;
2705 ctxt.format = format ? 1 : 0;
2706 ctxt.encoding = (const xmlChar *) encoding;
2707 xmlSaveCtxtInit(&ctxt);
2708 ctxt.options |= XML_SAVE_AS_XML;
2709
2710 xmlDocContentDumpOutput(&ctxt, cur);
2711
2712 ret = xmlOutputBufferClose(buf);
2713 return(ret);
2714 }
2715
2716
2717 /**
2718 * xmlSaveFileEnc:
2719 * @filename: the filename (or URL)
2720 * @cur: the document
2721 * @encoding: the name of an encoding (or NULL)
2722 *
2723 * Dump an XML document, converting it to the given encoding
2724 *
2725 * returns: the number of bytes written or -1 in case of failure.
2726 */
2727 int
xmlSaveFileEnc(const char * filename,xmlDocPtr cur,const char * encoding)2728 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2729 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2730 }
2731
2732 /**
2733 * xmlSaveFormatFile:
2734 * @filename: the filename (or URL)
2735 * @cur: the document
2736 * @format: should formatting spaces been added
2737 *
2738 * Dump an XML document to a file. Will use compression if
2739 * compiled in and enabled. If @filename is "-" the stdout file is
2740 * used. If @format is set then the document will be indented on output.
2741 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2742 * or xmlKeepBlanksDefault(0) was called
2743 *
2744 * returns: the number of bytes written or -1 in case of failure.
2745 */
2746 int
xmlSaveFormatFile(const char * filename,xmlDocPtr cur,int format)2747 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2748 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2749 }
2750
2751 /**
2752 * xmlSaveFile:
2753 * @filename: the filename (or URL)
2754 * @cur: the document
2755 *
2756 * Dump an XML document to a file. Will use compression if
2757 * compiled in and enabled. If @filename is "-" the stdout file is
2758 * used.
2759 * returns: the number of bytes written or -1 in case of failure.
2760 */
2761 int
xmlSaveFile(const char * filename,xmlDocPtr cur)2762 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2763 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2764 }
2765
2766 #endif /* LIBXML_OUTPUT_ENABLED */
2767
2768 #define bottom_xmlsave
2769 #include "elfgcchack.h"
2770