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 information
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 information
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 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur);
594 void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur);
595 static int xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur);
596
597 /**
598 * xmlOutputBufferWriteWSNonSig:
599 * @ctxt: The save context
600 * @extra: Number of extra indents to apply to ctxt->level
601 *
602 * Write out formatting for non-significant whitespace output.
603 */
604 static void
xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt,int extra)605 xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra)
606 {
607 int i;
608 if ((ctxt == NULL) || (ctxt->buf == NULL))
609 return;
610 xmlOutputBufferWrite(ctxt->buf, 1, "\n");
611 for (i = 0; i < (ctxt->level + extra); i += ctxt->indent_nr) {
612 xmlOutputBufferWrite(ctxt->buf, ctxt->indent_size *
613 ((ctxt->level + extra - i) > ctxt->indent_nr ?
614 ctxt->indent_nr : (ctxt->level + extra - i)),
615 ctxt->indent);
616 }
617 }
618
619 /**
620 * xmlNsDumpOutput:
621 * @buf: the XML buffer output
622 * @cur: a namespace
623 * @ctxt: the output save context. Optional.
624 *
625 * Dump a local Namespace definition.
626 * Should be called in the context of attributes dumps.
627 * If @ctxt is supplied, @buf should be its buffer.
628 */
629 static void
xmlNsDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur,xmlSaveCtxtPtr ctxt)630 xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) {
631 if ((cur == NULL) || (buf == NULL)) return;
632 if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) {
633 if (xmlStrEqual(cur->prefix, BAD_CAST "xml"))
634 return;
635
636 if (ctxt != NULL && ctxt->format == 2)
637 xmlOutputBufferWriteWSNonSig(ctxt, 2);
638 else
639 xmlOutputBufferWrite(buf, 1, " ");
640
641 /* Within the context of an element attributes */
642 if (cur->prefix != NULL) {
643 xmlOutputBufferWrite(buf, 6, "xmlns:");
644 xmlOutputBufferWriteString(buf, (const char *)cur->prefix);
645 } else
646 xmlOutputBufferWrite(buf, 5, "xmlns");
647 xmlOutputBufferWrite(buf, 1, "=");
648 xmlBufWriteQuotedString(buf->buffer, cur->href);
649 }
650 }
651
652 /**
653 * xmlNsDumpOutputCtxt
654 * @ctxt: the save context
655 * @cur: a namespace
656 *
657 * Dump a local Namespace definition to a save context.
658 * Should be called in the context of attribute dumps.
659 */
660 static void
xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)661 xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
662 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
663 }
664
665 /**
666 * xmlNsListDumpOutputCtxt
667 * @ctxt: the save context
668 * @cur: the first namespace
669 *
670 * Dump a list of local namespace definitions to a save context.
671 * Should be called in the context of attribute dumps.
672 */
673 static void
xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt,xmlNsPtr cur)674 xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) {
675 while (cur != NULL) {
676 xmlNsDumpOutput(ctxt->buf, cur, ctxt);
677 cur = cur->next;
678 }
679 }
680
681 /**
682 * xmlNsListDumpOutput:
683 * @buf: the XML buffer output
684 * @cur: the first namespace
685 *
686 * Dump a list of local Namespace definitions.
687 * Should be called in the context of attributes dumps.
688 */
689 void
xmlNsListDumpOutput(xmlOutputBufferPtr buf,xmlNsPtr cur)690 xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) {
691 while (cur != NULL) {
692 xmlNsDumpOutput(buf, cur, NULL);
693 cur = cur->next;
694 }
695 }
696
697 /**
698 * xmlDtdDumpOutput:
699 * @buf: the XML buffer output
700 * @dtd: the pointer to the DTD
701 *
702 * Dump the XML document DTD, if any.
703 */
704 static void
xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt,xmlDtdPtr dtd)705 xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) {
706 xmlOutputBufferPtr buf;
707 xmlNodePtr cur;
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 for (cur = dtd->children; cur != NULL; cur = cur->next) {
746 xmlNodeDumpOutputInternal(ctxt, cur);
747 }
748 ctxt->format = format;
749 ctxt->level = level;
750 xmlOutputBufferWrite(buf, 2, "]>");
751 }
752
753 /**
754 * xmlAttrDumpOutput:
755 * @buf: the XML buffer output
756 * @cur: the attribute pointer
757 *
758 * Dump an XML attribute
759 */
760 static void
xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)761 xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
762 xmlOutputBufferPtr buf;
763
764 if (cur == NULL) return;
765 buf = ctxt->buf;
766 if (buf == NULL) return;
767 if (ctxt->format == 2)
768 xmlOutputBufferWriteWSNonSig(ctxt, 2);
769 else
770 xmlOutputBufferWrite(buf, 1, " ");
771 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
772 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
773 xmlOutputBufferWrite(buf, 1, ":");
774 }
775 xmlOutputBufferWriteString(buf, (const char *)cur->name);
776 xmlOutputBufferWrite(buf, 2, "=\"");
777 xmlAttrSerializeContent(buf, cur);
778 xmlOutputBufferWrite(buf, 1, "\"");
779 }
780
781 #ifdef LIBXML_HTML_ENABLED
782 /**
783 * htmlNodeDumpOutputInternal:
784 * @cur: the current node
785 *
786 * Dump an HTML node, recursive behaviour, children are printed too.
787 */
788 static int
htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)789 htmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
790 const xmlChar *oldenc = NULL;
791 const xmlChar *oldctxtenc = ctxt->encoding;
792 const xmlChar *encoding = ctxt->encoding;
793 xmlOutputBufferPtr buf = ctxt->buf;
794 int switched_encoding = 0;
795 xmlDocPtr doc;
796
797 xmlInitParser();
798
799 doc = cur->doc;
800 if (doc != NULL) {
801 oldenc = doc->encoding;
802 if (ctxt->encoding != NULL) {
803 doc->encoding = BAD_CAST ctxt->encoding;
804 } else if (doc->encoding != NULL) {
805 encoding = doc->encoding;
806 }
807 }
808
809 if ((encoding != NULL) && (doc != NULL))
810 htmlSetMetaEncoding(doc, (const xmlChar *) encoding);
811 if ((encoding == NULL) && (doc != NULL))
812 encoding = htmlGetMetaEncoding(doc);
813 if (encoding == NULL)
814 encoding = BAD_CAST "HTML";
815 if ((encoding != NULL) && (oldctxtenc == NULL) &&
816 (buf->encoder == NULL) && (buf->conv == NULL)) {
817 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
818 doc->encoding = oldenc;
819 return(-1);
820 }
821 switched_encoding = 1;
822 }
823 if (ctxt->options & XML_SAVE_FORMAT)
824 htmlNodeDumpFormatOutput(buf, doc, cur,
825 (const char *)encoding, 1);
826 else
827 htmlNodeDumpFormatOutput(buf, doc, cur,
828 (const char *)encoding, 0);
829 /*
830 * Restore the state of the saving context at the end of the document
831 */
832 if ((switched_encoding) && (oldctxtenc == NULL)) {
833 xmlSaveClearEncoding(ctxt);
834 }
835 if (doc != NULL)
836 doc->encoding = oldenc;
837 return(0);
838 }
839 #endif
840
841 /**
842 * xmlNodeDumpOutputInternal:
843 * @cur: the current node
844 *
845 * Dump an XML node, recursive behaviour, children are printed too.
846 */
847 static void
xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)848 xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
849 int format = ctxt->format;
850 xmlNodePtr tmp, root, unformattedNode = NULL;
851 xmlAttrPtr attr;
852 xmlChar *start, *end;
853 xmlOutputBufferPtr buf;
854
855 if (cur == NULL) return;
856 buf = ctxt->buf;
857
858 root = cur;
859 while (1) {
860 switch (cur->type) {
861 case XML_DOCUMENT_NODE:
862 case XML_HTML_DOCUMENT_NODE:
863 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
864 break;
865
866 case XML_DTD_NODE:
867 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
868 break;
869
870 case XML_DOCUMENT_FRAG_NODE:
871 if (cur->children != NULL) {
872 cur = cur->children;
873 continue;
874 }
875 break;
876
877 case XML_ELEMENT_DECL:
878 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
879 break;
880
881 case XML_ATTRIBUTE_DECL:
882 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
883 break;
884
885 case XML_ENTITY_DECL:
886 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
887 break;
888
889 case XML_ELEMENT_NODE:
890 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
891 xmlOutputBufferWrite(buf, ctxt->indent_size *
892 (ctxt->level > ctxt->indent_nr ?
893 ctxt->indent_nr : ctxt->level),
894 ctxt->indent);
895
896 xmlOutputBufferWrite(buf, 1, "<");
897 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
898 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
899 xmlOutputBufferWrite(buf, 1, ":");
900 }
901 xmlOutputBufferWriteString(buf, (const char *)cur->name);
902 if (cur->nsDef)
903 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
904 for (attr = cur->properties; attr != NULL; attr = attr->next)
905 xmlAttrDumpOutput(ctxt, attr);
906
907 if (cur->children == NULL) {
908 if ((ctxt->options & XML_SAVE_NO_EMPTY) == 0) {
909 if (ctxt->format == 2)
910 xmlOutputBufferWriteWSNonSig(ctxt, 0);
911 xmlOutputBufferWrite(buf, 2, "/>");
912 } else {
913 if (ctxt->format == 2)
914 xmlOutputBufferWriteWSNonSig(ctxt, 1);
915 xmlOutputBufferWrite(buf, 3, "></");
916 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
917 xmlOutputBufferWriteString(buf,
918 (const char *)cur->ns->prefix);
919 xmlOutputBufferWrite(buf, 1, ":");
920 }
921 xmlOutputBufferWriteString(buf, (const char *)cur->name);
922 if (ctxt->format == 2)
923 xmlOutputBufferWriteWSNonSig(ctxt, 0);
924 xmlOutputBufferWrite(buf, 1, ">");
925 }
926 } else {
927 if (ctxt->format == 1) {
928 tmp = cur->children;
929 while (tmp != NULL) {
930 if ((tmp->type == XML_TEXT_NODE) ||
931 (tmp->type == XML_CDATA_SECTION_NODE) ||
932 (tmp->type == XML_ENTITY_REF_NODE)) {
933 ctxt->format = 0;
934 unformattedNode = cur;
935 break;
936 }
937 tmp = tmp->next;
938 }
939 }
940 if (ctxt->format == 2)
941 xmlOutputBufferWriteWSNonSig(ctxt, 1);
942 xmlOutputBufferWrite(buf, 1, ">");
943 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
944 if (ctxt->level >= 0) ctxt->level++;
945 cur = cur->children;
946 continue;
947 }
948
949 break;
950
951 case XML_TEXT_NODE:
952 if (cur->content == NULL)
953 break;
954 if (cur->name != xmlStringTextNoenc) {
955 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
956 } else {
957 /*
958 * Disable escaping, needed for XSLT
959 */
960 xmlOutputBufferWriteString(buf, (const char *) cur->content);
961 }
962 break;
963
964 case XML_PI_NODE:
965 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
966 xmlOutputBufferWrite(buf, ctxt->indent_size *
967 (ctxt->level > ctxt->indent_nr ?
968 ctxt->indent_nr : ctxt->level),
969 ctxt->indent);
970
971 if (cur->content != NULL) {
972 xmlOutputBufferWrite(buf, 2, "<?");
973 xmlOutputBufferWriteString(buf, (const char *)cur->name);
974 if (cur->content != NULL) {
975 if (ctxt->format == 2)
976 xmlOutputBufferWriteWSNonSig(ctxt, 0);
977 else
978 xmlOutputBufferWrite(buf, 1, " ");
979 xmlOutputBufferWriteString(buf,
980 (const char *)cur->content);
981 }
982 xmlOutputBufferWrite(buf, 2, "?>");
983 } else {
984 xmlOutputBufferWrite(buf, 2, "<?");
985 xmlOutputBufferWriteString(buf, (const char *)cur->name);
986 if (ctxt->format == 2)
987 xmlOutputBufferWriteWSNonSig(ctxt, 0);
988 xmlOutputBufferWrite(buf, 2, "?>");
989 }
990 break;
991
992 case XML_COMMENT_NODE:
993 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
994 xmlOutputBufferWrite(buf, ctxt->indent_size *
995 (ctxt->level > ctxt->indent_nr ?
996 ctxt->indent_nr : ctxt->level),
997 ctxt->indent);
998
999 if (cur->content != NULL) {
1000 xmlOutputBufferWrite(buf, 4, "<!--");
1001 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1002 xmlOutputBufferWrite(buf, 3, "-->");
1003 }
1004 break;
1005
1006 case XML_ENTITY_REF_NODE:
1007 xmlOutputBufferWrite(buf, 1, "&");
1008 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1009 xmlOutputBufferWrite(buf, 1, ";");
1010 break;
1011
1012 case XML_CDATA_SECTION_NODE:
1013 if (cur->content == NULL || *cur->content == '\0') {
1014 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1015 } else {
1016 start = end = cur->content;
1017 while (*end != '\0') {
1018 if ((*end == ']') && (*(end + 1) == ']') &&
1019 (*(end + 2) == '>')) {
1020 end = end + 2;
1021 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1022 xmlOutputBufferWrite(buf, end - start,
1023 (const char *)start);
1024 xmlOutputBufferWrite(buf, 3, "]]>");
1025 start = end;
1026 }
1027 end++;
1028 }
1029 if (start != end) {
1030 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1031 xmlOutputBufferWriteString(buf, (const char *)start);
1032 xmlOutputBufferWrite(buf, 3, "]]>");
1033 }
1034 }
1035 break;
1036
1037 case XML_ATTRIBUTE_NODE:
1038 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1039 break;
1040
1041 case XML_NAMESPACE_DECL:
1042 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1043 break;
1044
1045 default:
1046 break;
1047 }
1048
1049 while (1) {
1050 if (cur == root)
1051 return;
1052 if ((ctxt->format == 1) &&
1053 (cur->type != XML_XINCLUDE_START) &&
1054 (cur->type != XML_XINCLUDE_END))
1055 xmlOutputBufferWrite(buf, 1, "\n");
1056 if (cur->next != NULL) {
1057 cur = cur->next;
1058 break;
1059 }
1060
1061 /*
1062 * The parent should never be NULL here but we want to handle
1063 * corrupted documents gracefully.
1064 */
1065 if (cur->parent == NULL)
1066 return;
1067 cur = cur->parent;
1068
1069 if (cur->type == XML_ELEMENT_NODE) {
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,
1080 (const char *)cur->ns->prefix);
1081 xmlOutputBufferWrite(buf, 1, ":");
1082 }
1083
1084 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1085 if (ctxt->format == 2)
1086 xmlOutputBufferWriteWSNonSig(ctxt, 0);
1087 xmlOutputBufferWrite(buf, 1, ">");
1088
1089 if (cur == unformattedNode) {
1090 ctxt->format = format;
1091 unformattedNode = NULL;
1092 }
1093 }
1094 }
1095 }
1096 }
1097
1098 /**
1099 * xmlDocContentDumpOutput:
1100 * @cur: the document
1101 *
1102 * Dump an XML document.
1103 */
1104 static int
xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt,xmlDocPtr cur)1105 xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) {
1106 #ifdef LIBXML_HTML_ENABLED
1107 xmlDtdPtr dtd;
1108 int is_xhtml = 0;
1109 #endif
1110 const xmlChar *oldenc = cur->encoding;
1111 const xmlChar *oldctxtenc = ctxt->encoding;
1112 const xmlChar *encoding = ctxt->encoding;
1113 xmlCharEncodingOutputFunc oldescape = ctxt->escape;
1114 xmlCharEncodingOutputFunc oldescapeAttr = ctxt->escapeAttr;
1115 xmlOutputBufferPtr buf = ctxt->buf;
1116 xmlCharEncoding enc;
1117 int switched_encoding = 0;
1118
1119 xmlInitParser();
1120
1121 if ((cur->type != XML_HTML_DOCUMENT_NODE) &&
1122 (cur->type != XML_DOCUMENT_NODE))
1123 return(-1);
1124
1125 if (ctxt->encoding != NULL) {
1126 cur->encoding = BAD_CAST ctxt->encoding;
1127 } else if (cur->encoding != NULL) {
1128 encoding = cur->encoding;
1129 }
1130
1131 if (((cur->type == XML_HTML_DOCUMENT_NODE) &&
1132 ((ctxt->options & XML_SAVE_AS_XML) == 0) &&
1133 ((ctxt->options & XML_SAVE_XHTML) == 0)) ||
1134 (ctxt->options & XML_SAVE_AS_HTML)) {
1135 #ifdef LIBXML_HTML_ENABLED
1136 if (encoding != NULL)
1137 htmlSetMetaEncoding(cur, (const xmlChar *) encoding);
1138 if (encoding == NULL)
1139 encoding = htmlGetMetaEncoding(cur);
1140 if (encoding == NULL)
1141 encoding = BAD_CAST "HTML";
1142 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1143 (buf->encoder == NULL) && (buf->conv == NULL)) {
1144 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1145 cur->encoding = oldenc;
1146 return(-1);
1147 }
1148 }
1149 if (ctxt->options & XML_SAVE_FORMAT)
1150 htmlDocContentDumpFormatOutput(buf, cur,
1151 (const char *)encoding, 1);
1152 else
1153 htmlDocContentDumpFormatOutput(buf, cur,
1154 (const char *)encoding, 0);
1155 if (ctxt->encoding != NULL)
1156 cur->encoding = oldenc;
1157 return(0);
1158 #else
1159 return(-1);
1160 #endif
1161 } else if ((cur->type == XML_DOCUMENT_NODE) ||
1162 (ctxt->options & XML_SAVE_AS_XML) ||
1163 (ctxt->options & XML_SAVE_XHTML)) {
1164 enc = xmlParseCharEncoding((const char*) encoding);
1165 if ((encoding != NULL) && (oldctxtenc == NULL) &&
1166 (buf->encoder == NULL) && (buf->conv == NULL) &&
1167 ((ctxt->options & XML_SAVE_NO_DECL) == 0)) {
1168 if ((enc != XML_CHAR_ENCODING_UTF8) &&
1169 (enc != XML_CHAR_ENCODING_NONE) &&
1170 (enc != XML_CHAR_ENCODING_ASCII)) {
1171 /*
1172 * we need to switch to this encoding but just for this
1173 * document since we output the XMLDecl the conversion
1174 * must be done to not generate not well formed documents.
1175 */
1176 if (xmlSaveSwitchEncoding(ctxt, (const char*) encoding) < 0) {
1177 cur->encoding = oldenc;
1178 return(-1);
1179 }
1180 switched_encoding = 1;
1181 }
1182 if (ctxt->escape == xmlEscapeEntities)
1183 ctxt->escape = NULL;
1184 if (ctxt->escapeAttr == xmlEscapeEntities)
1185 ctxt->escapeAttr = NULL;
1186 }
1187
1188
1189 /*
1190 * Save the XML declaration
1191 */
1192 if ((ctxt->options & XML_SAVE_NO_DECL) == 0) {
1193 xmlOutputBufferWrite(buf, 14, "<?xml version=");
1194 if (cur->version != NULL)
1195 xmlBufWriteQuotedString(buf->buffer, cur->version);
1196 else
1197 xmlOutputBufferWrite(buf, 5, "\"1.0\"");
1198 if (encoding != NULL) {
1199 xmlOutputBufferWrite(buf, 10, " encoding=");
1200 xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding);
1201 }
1202 switch (cur->standalone) {
1203 case 0:
1204 xmlOutputBufferWrite(buf, 16, " standalone=\"no\"");
1205 break;
1206 case 1:
1207 xmlOutputBufferWrite(buf, 17, " standalone=\"yes\"");
1208 break;
1209 }
1210 xmlOutputBufferWrite(buf, 3, "?>\n");
1211 }
1212
1213 #ifdef LIBXML_HTML_ENABLED
1214 if (ctxt->options & XML_SAVE_XHTML)
1215 is_xhtml = 1;
1216 if ((ctxt->options & XML_SAVE_NO_XHTML) == 0) {
1217 dtd = xmlGetIntSubset(cur);
1218 if (dtd != NULL) {
1219 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
1220 if (is_xhtml < 0) is_xhtml = 0;
1221 }
1222 }
1223 #endif
1224 if (cur->children != NULL) {
1225 xmlNodePtr child = cur->children;
1226
1227 while (child != NULL) {
1228 ctxt->level = 0;
1229 #ifdef LIBXML_HTML_ENABLED
1230 if (is_xhtml)
1231 xhtmlNodeDumpOutput(ctxt, child);
1232 else
1233 #endif
1234 xmlNodeDumpOutputInternal(ctxt, child);
1235 if ((child->type != XML_XINCLUDE_START) &&
1236 (child->type != XML_XINCLUDE_END))
1237 xmlOutputBufferWrite(buf, 1, "\n");
1238 child = child->next;
1239 }
1240 }
1241 }
1242
1243 /*
1244 * Restore the state of the saving context at the end of the document
1245 */
1246 if ((switched_encoding) && (oldctxtenc == NULL)) {
1247 xmlSaveClearEncoding(ctxt);
1248 ctxt->escape = oldescape;
1249 ctxt->escapeAttr = oldescapeAttr;
1250 }
1251 cur->encoding = oldenc;
1252 return(0);
1253 }
1254
1255 #ifdef LIBXML_HTML_ENABLED
1256 /************************************************************************
1257 * *
1258 * Functions specific to XHTML serialization *
1259 * *
1260 ************************************************************************/
1261
1262 /**
1263 * xhtmlIsEmpty:
1264 * @node: the node
1265 *
1266 * Check if a node is an empty xhtml node
1267 *
1268 * Returns 1 if the node is an empty node, 0 if not and -1 in case of error
1269 */
1270 static int
xhtmlIsEmpty(xmlNodePtr node)1271 xhtmlIsEmpty(xmlNodePtr node) {
1272 if (node == NULL)
1273 return(-1);
1274 if (node->type != XML_ELEMENT_NODE)
1275 return(0);
1276 if ((node->ns != NULL) && (!xmlStrEqual(node->ns->href, XHTML_NS_NAME)))
1277 return(0);
1278 if (node->children != NULL)
1279 return(0);
1280 switch (node->name[0]) {
1281 case 'a':
1282 if (xmlStrEqual(node->name, BAD_CAST "area"))
1283 return(1);
1284 return(0);
1285 case 'b':
1286 if (xmlStrEqual(node->name, BAD_CAST "br"))
1287 return(1);
1288 if (xmlStrEqual(node->name, BAD_CAST "base"))
1289 return(1);
1290 if (xmlStrEqual(node->name, BAD_CAST "basefont"))
1291 return(1);
1292 return(0);
1293 case 'c':
1294 if (xmlStrEqual(node->name, BAD_CAST "col"))
1295 return(1);
1296 return(0);
1297 case 'f':
1298 if (xmlStrEqual(node->name, BAD_CAST "frame"))
1299 return(1);
1300 return(0);
1301 case 'h':
1302 if (xmlStrEqual(node->name, BAD_CAST "hr"))
1303 return(1);
1304 return(0);
1305 case 'i':
1306 if (xmlStrEqual(node->name, BAD_CAST "img"))
1307 return(1);
1308 if (xmlStrEqual(node->name, BAD_CAST "input"))
1309 return(1);
1310 if (xmlStrEqual(node->name, BAD_CAST "isindex"))
1311 return(1);
1312 return(0);
1313 case 'l':
1314 if (xmlStrEqual(node->name, BAD_CAST "link"))
1315 return(1);
1316 return(0);
1317 case 'm':
1318 if (xmlStrEqual(node->name, BAD_CAST "meta"))
1319 return(1);
1320 return(0);
1321 case 'p':
1322 if (xmlStrEqual(node->name, BAD_CAST "param"))
1323 return(1);
1324 return(0);
1325 }
1326 return(0);
1327 }
1328
1329 /**
1330 * xhtmlAttrListDumpOutput:
1331 * @cur: the first attribute pointer
1332 *
1333 * Dump a list of XML attributes
1334 */
1335 static void
xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt,xmlAttrPtr cur)1336 xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) {
1337 xmlAttrPtr xml_lang = NULL;
1338 xmlAttrPtr lang = NULL;
1339 xmlAttrPtr name = NULL;
1340 xmlAttrPtr id = NULL;
1341 xmlNodePtr parent;
1342 xmlOutputBufferPtr buf;
1343
1344 if (cur == NULL) return;
1345 buf = ctxt->buf;
1346 parent = cur->parent;
1347 while (cur != NULL) {
1348 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "id")))
1349 id = cur;
1350 else
1351 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "name")))
1352 name = cur;
1353 else
1354 if ((cur->ns == NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")))
1355 lang = cur;
1356 else
1357 if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) &&
1358 (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml")))
1359 xml_lang = cur;
1360 else if ((cur->ns == NULL) &&
1361 ((cur->children == NULL) ||
1362 (cur->children->content == NULL) ||
1363 (cur->children->content[0] == 0)) &&
1364 (htmlIsBooleanAttr(cur->name))) {
1365 if (cur->children != NULL)
1366 xmlFreeNode(cur->children);
1367 cur->children = xmlNewText(cur->name);
1368 if (cur->children != NULL)
1369 cur->children->parent = (xmlNodePtr) cur;
1370 }
1371 xmlAttrDumpOutput(ctxt, cur);
1372 cur = cur->next;
1373 }
1374 /*
1375 * C.8
1376 */
1377 if ((name != NULL) && (id == NULL)) {
1378 if ((parent != NULL) && (parent->name != NULL) &&
1379 ((xmlStrEqual(parent->name, BAD_CAST "a")) ||
1380 (xmlStrEqual(parent->name, BAD_CAST "p")) ||
1381 (xmlStrEqual(parent->name, BAD_CAST "div")) ||
1382 (xmlStrEqual(parent->name, BAD_CAST "img")) ||
1383 (xmlStrEqual(parent->name, BAD_CAST "map")) ||
1384 (xmlStrEqual(parent->name, BAD_CAST "applet")) ||
1385 (xmlStrEqual(parent->name, BAD_CAST "form")) ||
1386 (xmlStrEqual(parent->name, BAD_CAST "frame")) ||
1387 (xmlStrEqual(parent->name, BAD_CAST "iframe")))) {
1388 xmlOutputBufferWrite(buf, 5, " id=\"");
1389 xmlAttrSerializeContent(buf, name);
1390 xmlOutputBufferWrite(buf, 1, "\"");
1391 }
1392 }
1393 /*
1394 * C.7.
1395 */
1396 if ((lang != NULL) && (xml_lang == NULL)) {
1397 xmlOutputBufferWrite(buf, 11, " xml:lang=\"");
1398 xmlAttrSerializeContent(buf, lang);
1399 xmlOutputBufferWrite(buf, 1, "\"");
1400 } else
1401 if ((xml_lang != NULL) && (lang == NULL)) {
1402 xmlOutputBufferWrite(buf, 7, " lang=\"");
1403 xmlAttrSerializeContent(buf, xml_lang);
1404 xmlOutputBufferWrite(buf, 1, "\"");
1405 }
1406 }
1407
1408 /**
1409 * xhtmlNodeDumpOutput:
1410 * @buf: the XML buffer output
1411 * @doc: the XHTML document
1412 * @cur: the current node
1413 * @level: the imbrication level for indenting
1414 * @format: is formatting allowed
1415 * @encoding: an optional encoding string
1416 *
1417 * Dump an XHTML node, recursive behaviour, children are printed too.
1418 */
1419 static void
xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1420 xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) {
1421 int format = ctxt->format, addmeta;
1422 xmlNodePtr tmp, root, unformattedNode = NULL;
1423 xmlChar *start, *end;
1424 xmlOutputBufferPtr buf = ctxt->buf;
1425
1426 if (cur == NULL) return;
1427
1428 root = cur;
1429 while (1) {
1430 switch (cur->type) {
1431 case XML_DOCUMENT_NODE:
1432 case XML_HTML_DOCUMENT_NODE:
1433 xmlDocContentDumpOutput(ctxt, (xmlDocPtr) cur);
1434 break;
1435
1436 case XML_NAMESPACE_DECL:
1437 xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur);
1438 break;
1439
1440 case XML_DTD_NODE:
1441 xmlDtdDumpOutput(ctxt, (xmlDtdPtr) cur);
1442 break;
1443
1444 case XML_DOCUMENT_FRAG_NODE:
1445 if (cur->children) {
1446 cur = cur->children;
1447 continue;
1448 }
1449 break;
1450
1451 case XML_ELEMENT_DECL:
1452 xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur);
1453 break;
1454
1455 case XML_ATTRIBUTE_DECL:
1456 xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur);
1457 break;
1458
1459 case XML_ENTITY_DECL:
1460 xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur);
1461 break;
1462
1463 case XML_ELEMENT_NODE:
1464 addmeta = 0;
1465
1466 if ((cur != root) && (ctxt->format == 1) && (xmlIndentTreeOutput))
1467 xmlOutputBufferWrite(buf, ctxt->indent_size *
1468 (ctxt->level > ctxt->indent_nr ?
1469 ctxt->indent_nr : ctxt->level),
1470 ctxt->indent);
1471
1472 xmlOutputBufferWrite(buf, 1, "<");
1473 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1474 xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix);
1475 xmlOutputBufferWrite(buf, 1, ":");
1476 }
1477
1478 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1479 if (cur->nsDef)
1480 xmlNsListDumpOutputCtxt(ctxt, cur->nsDef);
1481 if ((xmlStrEqual(cur->name, BAD_CAST "html") &&
1482 (cur->ns == NULL) && (cur->nsDef == NULL))) {
1483 /*
1484 * 3.1.1. Strictly Conforming Documents A.3.1.1 3/
1485 */
1486 xmlOutputBufferWriteString(buf,
1487 " xmlns=\"http://www.w3.org/1999/xhtml\"");
1488 }
1489 if (cur->properties != NULL)
1490 xhtmlAttrListDumpOutput(ctxt, cur->properties);
1491
1492 if ((cur->parent != NULL) &&
1493 (cur->parent->parent == (xmlNodePtr) cur->doc) &&
1494 xmlStrEqual(cur->name, BAD_CAST"head") &&
1495 xmlStrEqual(cur->parent->name, BAD_CAST"html")) {
1496
1497 tmp = cur->children;
1498 while (tmp != NULL) {
1499 if (xmlStrEqual(tmp->name, BAD_CAST"meta")) {
1500 xmlChar *httpequiv;
1501
1502 httpequiv = xmlGetProp(tmp, BAD_CAST"http-equiv");
1503 if (httpequiv != NULL) {
1504 if (xmlStrcasecmp(httpequiv,
1505 BAD_CAST"Content-Type") == 0) {
1506 xmlFree(httpequiv);
1507 break;
1508 }
1509 xmlFree(httpequiv);
1510 }
1511 }
1512 tmp = tmp->next;
1513 }
1514 if (tmp == NULL)
1515 addmeta = 1;
1516 }
1517
1518 if (cur->children == NULL) {
1519 if (((cur->ns == NULL) || (cur->ns->prefix == NULL)) &&
1520 ((xhtmlIsEmpty(cur) == 1) && (addmeta == 0))) {
1521 /*
1522 * C.2. Empty Elements
1523 */
1524 xmlOutputBufferWrite(buf, 3, " />");
1525 } else {
1526 if (addmeta == 1) {
1527 xmlOutputBufferWrite(buf, 1, ">");
1528 if (ctxt->format == 1) {
1529 xmlOutputBufferWrite(buf, 1, "\n");
1530 if (xmlIndentTreeOutput)
1531 xmlOutputBufferWrite(buf, ctxt->indent_size *
1532 (ctxt->level + 1 > ctxt->indent_nr ?
1533 ctxt->indent_nr : ctxt->level + 1),
1534 ctxt->indent);
1535 }
1536 xmlOutputBufferWriteString(buf,
1537 "<meta http-equiv=\"Content-Type\" "
1538 "content=\"text/html; charset=");
1539 if (ctxt->encoding) {
1540 xmlOutputBufferWriteString(buf,
1541 (const char *)ctxt->encoding);
1542 } else {
1543 xmlOutputBufferWrite(buf, 5, "UTF-8");
1544 }
1545 xmlOutputBufferWrite(buf, 4, "\" />");
1546 if (ctxt->format == 1)
1547 xmlOutputBufferWrite(buf, 1, "\n");
1548 } else {
1549 xmlOutputBufferWrite(buf, 1, ">");
1550 }
1551 /*
1552 * C.3. Element Minimization and Empty Element Content
1553 */
1554 xmlOutputBufferWrite(buf, 2, "</");
1555 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1556 xmlOutputBufferWriteString(buf,
1557 (const char *)cur->ns->prefix);
1558 xmlOutputBufferWrite(buf, 1, ":");
1559 }
1560 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1561 xmlOutputBufferWrite(buf, 1, ">");
1562 }
1563 } else {
1564 xmlOutputBufferWrite(buf, 1, ">");
1565 if (addmeta == 1) {
1566 if (ctxt->format == 1) {
1567 xmlOutputBufferWrite(buf, 1, "\n");
1568 if (xmlIndentTreeOutput)
1569 xmlOutputBufferWrite(buf, ctxt->indent_size *
1570 (ctxt->level + 1 > ctxt->indent_nr ?
1571 ctxt->indent_nr : ctxt->level + 1),
1572 ctxt->indent);
1573 }
1574 xmlOutputBufferWriteString(buf,
1575 "<meta http-equiv=\"Content-Type\" "
1576 "content=\"text/html; charset=");
1577 if (ctxt->encoding) {
1578 xmlOutputBufferWriteString(buf,
1579 (const char *)ctxt->encoding);
1580 } else {
1581 xmlOutputBufferWrite(buf, 5, "UTF-8");
1582 }
1583 xmlOutputBufferWrite(buf, 4, "\" />");
1584 }
1585
1586 if (ctxt->format == 1) {
1587 tmp = cur->children;
1588 while (tmp != NULL) {
1589 if ((tmp->type == XML_TEXT_NODE) ||
1590 (tmp->type == XML_ENTITY_REF_NODE)) {
1591 unformattedNode = cur;
1592 ctxt->format = 0;
1593 break;
1594 }
1595 tmp = tmp->next;
1596 }
1597 }
1598
1599 if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n");
1600 if (ctxt->level >= 0) ctxt->level++;
1601 cur = cur->children;
1602 continue;
1603 }
1604
1605 break;
1606
1607 case XML_TEXT_NODE:
1608 if (cur->content == NULL)
1609 break;
1610 if ((cur->name == xmlStringText) ||
1611 (cur->name != xmlStringTextNoenc)) {
1612 xmlOutputBufferWriteEscape(buf, cur->content, ctxt->escape);
1613 } else {
1614 /*
1615 * Disable escaping, needed for XSLT
1616 */
1617 xmlOutputBufferWriteString(buf, (const char *) cur->content);
1618 }
1619 break;
1620
1621 case XML_PI_NODE:
1622 if (cur->content != NULL) {
1623 xmlOutputBufferWrite(buf, 2, "<?");
1624 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1625 if (cur->content != NULL) {
1626 xmlOutputBufferWrite(buf, 1, " ");
1627 xmlOutputBufferWriteString(buf,
1628 (const char *)cur->content);
1629 }
1630 xmlOutputBufferWrite(buf, 2, "?>");
1631 } else {
1632 xmlOutputBufferWrite(buf, 2, "<?");
1633 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1634 xmlOutputBufferWrite(buf, 2, "?>");
1635 }
1636 break;
1637
1638 case XML_COMMENT_NODE:
1639 if (cur->content != NULL) {
1640 xmlOutputBufferWrite(buf, 4, "<!--");
1641 xmlOutputBufferWriteString(buf, (const char *)cur->content);
1642 xmlOutputBufferWrite(buf, 3, "-->");
1643 }
1644 break;
1645
1646 case XML_ENTITY_REF_NODE:
1647 xmlOutputBufferWrite(buf, 1, "&");
1648 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1649 xmlOutputBufferWrite(buf, 1, ";");
1650 break;
1651
1652 case XML_CDATA_SECTION_NODE:
1653 if (cur->content == NULL || *cur->content == '\0') {
1654 xmlOutputBufferWrite(buf, 12, "<![CDATA[]]>");
1655 } else {
1656 start = end = cur->content;
1657 while (*end != '\0') {
1658 if (*end == ']' && *(end + 1) == ']' &&
1659 *(end + 2) == '>') {
1660 end = end + 2;
1661 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1662 xmlOutputBufferWrite(buf, end - start,
1663 (const char *)start);
1664 xmlOutputBufferWrite(buf, 3, "]]>");
1665 start = end;
1666 }
1667 end++;
1668 }
1669 if (start != end) {
1670 xmlOutputBufferWrite(buf, 9, "<![CDATA[");
1671 xmlOutputBufferWriteString(buf, (const char *)start);
1672 xmlOutputBufferWrite(buf, 3, "]]>");
1673 }
1674 }
1675 break;
1676
1677 case XML_ATTRIBUTE_NODE:
1678 xmlAttrDumpOutput(ctxt, (xmlAttrPtr) cur);
1679 break;
1680
1681 default:
1682 break;
1683 }
1684
1685 while (1) {
1686 if (cur == root)
1687 return;
1688 if (ctxt->format == 1)
1689 xmlOutputBufferWrite(buf, 1, "\n");
1690 if (cur->next != NULL) {
1691 cur = cur->next;
1692 break;
1693 }
1694
1695 /*
1696 * The parent should never be NULL here but we want to handle
1697 * corrupted documents gracefully.
1698 */
1699 if (cur->parent == NULL)
1700 return;
1701 cur = cur->parent;
1702
1703 if (cur->type == XML_ELEMENT_NODE) {
1704 if (ctxt->level > 0) ctxt->level--;
1705 if ((xmlIndentTreeOutput) && (ctxt->format == 1))
1706 xmlOutputBufferWrite(buf, ctxt->indent_size *
1707 (ctxt->level > ctxt->indent_nr ?
1708 ctxt->indent_nr : ctxt->level),
1709 ctxt->indent);
1710
1711 xmlOutputBufferWrite(buf, 2, "</");
1712 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
1713 xmlOutputBufferWriteString(buf,
1714 (const char *)cur->ns->prefix);
1715 xmlOutputBufferWrite(buf, 1, ":");
1716 }
1717
1718 xmlOutputBufferWriteString(buf, (const char *)cur->name);
1719 xmlOutputBufferWrite(buf, 1, ">");
1720
1721 if (cur == unformattedNode) {
1722 ctxt->format = format;
1723 unformattedNode = NULL;
1724 }
1725 }
1726 }
1727 }
1728 }
1729 #endif
1730
1731 /************************************************************************
1732 * *
1733 * Public entry points *
1734 * *
1735 ************************************************************************/
1736
1737 /**
1738 * xmlSaveToFd:
1739 * @fd: a file descriptor number
1740 * @encoding: the encoding name to use or NULL
1741 * @options: a set of xmlSaveOptions
1742 *
1743 * Create a document saving context serializing to a file descriptor
1744 * with the encoding and the options given.
1745 *
1746 * Returns a new serialization context or NULL in case of error.
1747 */
1748 xmlSaveCtxtPtr
xmlSaveToFd(int fd,const char * encoding,int options)1749 xmlSaveToFd(int fd, const char *encoding, int options)
1750 {
1751 xmlSaveCtxtPtr ret;
1752
1753 ret = xmlNewSaveCtxt(encoding, options);
1754 if (ret == NULL) return(NULL);
1755 ret->buf = xmlOutputBufferCreateFd(fd, ret->handler);
1756 if (ret->buf == NULL) {
1757 xmlCharEncCloseFunc(ret->handler);
1758 xmlFreeSaveCtxt(ret);
1759 return(NULL);
1760 }
1761 return(ret);
1762 }
1763
1764 /**
1765 * xmlSaveToFilename:
1766 * @filename: a file name or an URL
1767 * @encoding: the encoding name to use or NULL
1768 * @options: a set of xmlSaveOptions
1769 *
1770 * Create a document saving context serializing to a filename or possibly
1771 * to an URL (but this is less reliable) with the encoding and the options
1772 * given.
1773 *
1774 * Returns a new serialization context or NULL in case of error.
1775 */
1776 xmlSaveCtxtPtr
xmlSaveToFilename(const char * filename,const char * encoding,int options)1777 xmlSaveToFilename(const char *filename, const char *encoding, int options)
1778 {
1779 xmlSaveCtxtPtr ret;
1780 int compression = 0; /* TODO handle compression option */
1781
1782 ret = xmlNewSaveCtxt(encoding, options);
1783 if (ret == NULL) return(NULL);
1784 ret->buf = xmlOutputBufferCreateFilename(filename, ret->handler,
1785 compression);
1786 if (ret->buf == NULL) {
1787 xmlCharEncCloseFunc(ret->handler);
1788 xmlFreeSaveCtxt(ret);
1789 return(NULL);
1790 }
1791 return(ret);
1792 }
1793
1794 /**
1795 * xmlSaveToBuffer:
1796 * @buffer: a buffer
1797 * @encoding: the encoding name to use or NULL
1798 * @options: a set of xmlSaveOptions
1799 *
1800 * Create a document saving context serializing to a buffer
1801 * with the encoding and the options given
1802 *
1803 * Returns a new serialization context or NULL in case of error.
1804 */
1805
1806 xmlSaveCtxtPtr
xmlSaveToBuffer(xmlBufferPtr buffer,const char * encoding,int options)1807 xmlSaveToBuffer(xmlBufferPtr buffer, const char *encoding, int options)
1808 {
1809 xmlSaveCtxtPtr ret;
1810
1811 ret = xmlNewSaveCtxt(encoding, options);
1812 if (ret == NULL) return(NULL);
1813 ret->buf = xmlOutputBufferCreateBuffer(buffer, ret->handler);
1814 if (ret->buf == NULL) {
1815 xmlCharEncCloseFunc(ret->handler);
1816 xmlFreeSaveCtxt(ret);
1817 return(NULL);
1818 }
1819 return(ret);
1820 }
1821
1822 /**
1823 * xmlSaveToIO:
1824 * @iowrite: an I/O write function
1825 * @ioclose: an I/O close function
1826 * @ioctx: an I/O handler
1827 * @encoding: the encoding name to use or NULL
1828 * @options: a set of xmlSaveOptions
1829 *
1830 * Create a document saving context serializing to a file descriptor
1831 * with the encoding and the options given
1832 *
1833 * Returns a new serialization context or NULL in case of error.
1834 */
1835 xmlSaveCtxtPtr
xmlSaveToIO(xmlOutputWriteCallback iowrite,xmlOutputCloseCallback ioclose,void * ioctx,const char * encoding,int options)1836 xmlSaveToIO(xmlOutputWriteCallback iowrite,
1837 xmlOutputCloseCallback ioclose,
1838 void *ioctx, const char *encoding, int options)
1839 {
1840 xmlSaveCtxtPtr ret;
1841
1842 ret = xmlNewSaveCtxt(encoding, options);
1843 if (ret == NULL) return(NULL);
1844 ret->buf = xmlOutputBufferCreateIO(iowrite, ioclose, ioctx, ret->handler);
1845 if (ret->buf == NULL) {
1846 xmlCharEncCloseFunc(ret->handler);
1847 xmlFreeSaveCtxt(ret);
1848 return(NULL);
1849 }
1850 return(ret);
1851 }
1852
1853 /**
1854 * xmlSaveDoc:
1855 * @ctxt: a document saving context
1856 * @doc: a document
1857 *
1858 * Save a full document to a saving context
1859 * TODO: The function is not fully implemented yet as it does not return the
1860 * byte count but 0 instead
1861 *
1862 * Returns the number of byte written or -1 in case of error
1863 */
1864 long
xmlSaveDoc(xmlSaveCtxtPtr ctxt,xmlDocPtr doc)1865 xmlSaveDoc(xmlSaveCtxtPtr ctxt, xmlDocPtr doc)
1866 {
1867 long ret = 0;
1868
1869 if ((ctxt == NULL) || (doc == NULL)) return(-1);
1870 if (xmlDocContentDumpOutput(ctxt, doc) < 0)
1871 return(-1);
1872 return(ret);
1873 }
1874
1875 /**
1876 * xmlSaveTree:
1877 * @ctxt: a document saving context
1878 * @node: the top node of the subtree to save
1879 *
1880 * Save a subtree starting at the node parameter to a saving context
1881 * TODO: The function is not fully implemented yet as it does not return the
1882 * byte count but 0 instead
1883 *
1884 * Returns the number of byte written or -1 in case of error
1885 */
1886 long
xmlSaveTree(xmlSaveCtxtPtr ctxt,xmlNodePtr cur)1887 xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur)
1888 {
1889 long ret = 0;
1890
1891 if ((ctxt == NULL) || (cur == NULL)) return(-1);
1892 #ifdef LIBXML_HTML_ENABLED
1893 if (ctxt->options & XML_SAVE_XHTML) {
1894 xhtmlNodeDumpOutput(ctxt, cur);
1895 return(ret);
1896 }
1897 if (((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL) &&
1898 (cur->doc->type == XML_HTML_DOCUMENT_NODE) &&
1899 ((ctxt->options & XML_SAVE_AS_XML) == 0)) ||
1900 (ctxt->options & XML_SAVE_AS_HTML)) {
1901 htmlNodeDumpOutputInternal(ctxt, cur);
1902 return(ret);
1903 }
1904 #endif
1905 xmlNodeDumpOutputInternal(ctxt, cur);
1906 return(ret);
1907 }
1908
1909 /**
1910 * xmlSaveFlush:
1911 * @ctxt: a document saving context
1912 *
1913 * Flush a document saving context, i.e. make sure that all bytes have
1914 * been output.
1915 *
1916 * Returns the number of byte written or -1 in case of error.
1917 */
1918 int
xmlSaveFlush(xmlSaveCtxtPtr ctxt)1919 xmlSaveFlush(xmlSaveCtxtPtr ctxt)
1920 {
1921 if (ctxt == NULL) return(-1);
1922 if (ctxt->buf == NULL) return(-1);
1923 return(xmlOutputBufferFlush(ctxt->buf));
1924 }
1925
1926 /**
1927 * xmlSaveClose:
1928 * @ctxt: a document saving context
1929 *
1930 * Close a document saving context, i.e. make sure that all bytes have
1931 * been output and free the associated data.
1932 *
1933 * Returns the number of byte written or -1 in case of error.
1934 */
1935 int
xmlSaveClose(xmlSaveCtxtPtr ctxt)1936 xmlSaveClose(xmlSaveCtxtPtr ctxt)
1937 {
1938 int ret;
1939
1940 if (ctxt == NULL) return(-1);
1941 ret = xmlSaveFlush(ctxt);
1942 xmlFreeSaveCtxt(ctxt);
1943 return(ret);
1944 }
1945
1946 /**
1947 * xmlSaveSetEscape:
1948 * @ctxt: a document saving context
1949 * @escape: the escaping function
1950 *
1951 * Set a custom escaping function to be used for text in element content
1952 *
1953 * Returns 0 if successful or -1 in case of error.
1954 */
1955 int
xmlSaveSetEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)1956 xmlSaveSetEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1957 {
1958 if (ctxt == NULL) return(-1);
1959 ctxt->escape = escape;
1960 return(0);
1961 }
1962
1963 /**
1964 * xmlSaveSetAttrEscape:
1965 * @ctxt: a document saving context
1966 * @escape: the escaping function
1967 *
1968 * Set a custom escaping function to be used for text in attribute content
1969 *
1970 * Returns 0 if successful or -1 in case of error.
1971 */
1972 int
xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt,xmlCharEncodingOutputFunc escape)1973 xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape)
1974 {
1975 if (ctxt == NULL) return(-1);
1976 ctxt->escapeAttr = escape;
1977 return(0);
1978 }
1979
1980 /************************************************************************
1981 * *
1982 * Public entry points based on buffers *
1983 * *
1984 ************************************************************************/
1985
1986 /**
1987 * xmlBufAttrSerializeTxtContent:
1988 * @buf: and xmlBufPtr output
1989 * @doc: the document
1990 * @attr: the attribute node
1991 * @string: the text content
1992 *
1993 * Serialize text attribute values to an xmlBufPtr
1994 */
1995 void
xmlBufAttrSerializeTxtContent(xmlBufPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)1996 xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc,
1997 xmlAttrPtr attr, const xmlChar * string)
1998 {
1999 xmlChar *base, *cur;
2000
2001 if (string == NULL)
2002 return;
2003 base = cur = (xmlChar *) string;
2004 while (*cur != 0) {
2005 if (*cur == '\n') {
2006 if (base != cur)
2007 xmlBufAdd(buf, base, cur - base);
2008 xmlBufAdd(buf, BAD_CAST " ", 5);
2009 cur++;
2010 base = cur;
2011 } else if (*cur == '\r') {
2012 if (base != cur)
2013 xmlBufAdd(buf, base, cur - base);
2014 xmlBufAdd(buf, BAD_CAST " ", 5);
2015 cur++;
2016 base = cur;
2017 } else if (*cur == '\t') {
2018 if (base != cur)
2019 xmlBufAdd(buf, base, cur - base);
2020 xmlBufAdd(buf, BAD_CAST "	", 4);
2021 cur++;
2022 base = cur;
2023 } else if (*cur == '"') {
2024 if (base != cur)
2025 xmlBufAdd(buf, base, cur - base);
2026 xmlBufAdd(buf, BAD_CAST """, 6);
2027 cur++;
2028 base = cur;
2029 } else if (*cur == '<') {
2030 if (base != cur)
2031 xmlBufAdd(buf, base, cur - base);
2032 xmlBufAdd(buf, BAD_CAST "<", 4);
2033 cur++;
2034 base = cur;
2035 } else if (*cur == '>') {
2036 if (base != cur)
2037 xmlBufAdd(buf, base, cur - base);
2038 xmlBufAdd(buf, BAD_CAST ">", 4);
2039 cur++;
2040 base = cur;
2041 } else if (*cur == '&') {
2042 if (base != cur)
2043 xmlBufAdd(buf, base, cur - base);
2044 xmlBufAdd(buf, BAD_CAST "&", 5);
2045 cur++;
2046 base = cur;
2047 } else if ((*cur >= 0x80) && (cur[1] != 0) &&
2048 ((doc == NULL) || (doc->encoding == NULL))) {
2049 /*
2050 * We assume we have UTF-8 content.
2051 */
2052 unsigned char tmp[12];
2053 int val = 0, l = 1;
2054
2055 if (base != cur)
2056 xmlBufAdd(buf, base, cur - base);
2057 if (*cur < 0xC0) {
2058 xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL);
2059 xmlSerializeHexCharRef(tmp, *cur);
2060 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2061 cur++;
2062 base = cur;
2063 continue;
2064 } else if (*cur < 0xE0) {
2065 val = (cur[0]) & 0x1F;
2066 val <<= 6;
2067 val |= (cur[1]) & 0x3F;
2068 l = 2;
2069 } else if ((*cur < 0xF0) && (cur [2] != 0)) {
2070 val = (cur[0]) & 0x0F;
2071 val <<= 6;
2072 val |= (cur[1]) & 0x3F;
2073 val <<= 6;
2074 val |= (cur[2]) & 0x3F;
2075 l = 3;
2076 } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) {
2077 val = (cur[0]) & 0x07;
2078 val <<= 6;
2079 val |= (cur[1]) & 0x3F;
2080 val <<= 6;
2081 val |= (cur[2]) & 0x3F;
2082 val <<= 6;
2083 val |= (cur[3]) & 0x3F;
2084 l = 4;
2085 }
2086 if ((l == 1) || (!IS_CHAR(val))) {
2087 xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL);
2088 xmlSerializeHexCharRef(tmp, *cur);
2089 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2090 cur++;
2091 base = cur;
2092 continue;
2093 }
2094 /*
2095 * We could do multiple things here. Just save
2096 * as a char ref
2097 */
2098 xmlSerializeHexCharRef(tmp, val);
2099 xmlBufAdd(buf, (xmlChar *) tmp, -1);
2100 cur += l;
2101 base = cur;
2102 } else {
2103 cur++;
2104 }
2105 }
2106 if (base != cur)
2107 xmlBufAdd(buf, base, cur - base);
2108 }
2109
2110 /**
2111 * xmlAttrSerializeTxtContent:
2112 * @buf: the XML buffer output
2113 * @doc: the document
2114 * @attr: the attribute node
2115 * @string: the text content
2116 *
2117 * Serialize text attribute values to an xml simple buffer
2118 */
2119 void
xmlAttrSerializeTxtContent(xmlBufferPtr buf,xmlDocPtr doc,xmlAttrPtr attr,const xmlChar * string)2120 xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc,
2121 xmlAttrPtr attr, const xmlChar * string)
2122 {
2123 xmlBufPtr buffer;
2124
2125 if ((buf == NULL) || (string == NULL))
2126 return;
2127 buffer = xmlBufFromBuffer(buf);
2128 if (buffer == NULL)
2129 return;
2130 xmlBufAttrSerializeTxtContent(buffer, doc, attr, string);
2131 xmlBufBackToBuffer(buffer);
2132 }
2133
2134 /**
2135 * xmlNodeDump:
2136 * @buf: the XML buffer output
2137 * @doc: the document
2138 * @cur: the current node
2139 * @level: the imbrication level for indenting
2140 * @format: is formatting allowed
2141 *
2142 * Dump an XML node, recursive behaviour,children are printed too.
2143 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2144 * or xmlKeepBlanksDefault(0) was called.
2145 * Since this is using xmlBuffer structures it is limited to 2GB and somehow
2146 * deprecated, use xmlNodeDumpOutput() instead.
2147 *
2148 * Returns the number of bytes written to the buffer or -1 in case of error
2149 */
2150 int
xmlNodeDump(xmlBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2151 xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2152 int format)
2153 {
2154 xmlBufPtr buffer;
2155 size_t ret;
2156
2157 if ((buf == NULL) || (cur == NULL))
2158 return(-1);
2159 buffer = xmlBufFromBuffer(buf);
2160 if (buffer == NULL)
2161 return(-1);
2162 ret = xmlBufNodeDump(buffer, doc, cur, level, format);
2163 xmlBufBackToBuffer(buffer);
2164 if (ret > INT_MAX)
2165 return(-1);
2166 return((int) ret);
2167 }
2168
2169 /**
2170 * xmlBufNodeDump:
2171 * @buf: the XML buffer output
2172 * @doc: the document
2173 * @cur: the current node
2174 * @level: the imbrication level for indenting
2175 * @format: is formatting allowed
2176 *
2177 * Dump an XML node, recursive behaviour,children are printed too.
2178 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2179 * or xmlKeepBlanksDefault(0) was called
2180 *
2181 * Returns the number of bytes written to the buffer, in case of error 0
2182 * is returned or @buf stores the error
2183 */
2184
2185 size_t
xmlBufNodeDump(xmlBufPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format)2186 xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level,
2187 int format)
2188 {
2189 size_t use;
2190 int ret;
2191 xmlOutputBufferPtr outbuf;
2192 int oldalloc;
2193
2194 xmlInitParser();
2195
2196 if (cur == NULL) {
2197 #ifdef DEBUG_TREE
2198 xmlGenericError(xmlGenericErrorContext,
2199 "xmlNodeDump : node == NULL\n");
2200 #endif
2201 return (-1);
2202 }
2203 if (buf == NULL) {
2204 #ifdef DEBUG_TREE
2205 xmlGenericError(xmlGenericErrorContext,
2206 "xmlNodeDump : buf == NULL\n");
2207 #endif
2208 return (-1);
2209 }
2210 outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2211 if (outbuf == NULL) {
2212 xmlSaveErrMemory("creating buffer");
2213 return (-1);
2214 }
2215 memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer));
2216 outbuf->buffer = buf;
2217 outbuf->encoder = NULL;
2218 outbuf->writecallback = NULL;
2219 outbuf->closecallback = NULL;
2220 outbuf->context = NULL;
2221 outbuf->written = 0;
2222
2223 use = xmlBufUse(buf);
2224 oldalloc = xmlBufGetAllocationScheme(buf);
2225 xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
2226 xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL);
2227 xmlBufSetAllocationScheme(buf, oldalloc);
2228 xmlFree(outbuf);
2229 ret = xmlBufUse(buf) - use;
2230 return (ret);
2231 }
2232
2233 /**
2234 * xmlElemDump:
2235 * @f: the FILE * for the output
2236 * @doc: the document
2237 * @cur: the current node
2238 *
2239 * Dump an XML/HTML node, recursive behaviour, children are printed too.
2240 */
2241 void
xmlElemDump(FILE * f,xmlDocPtr doc,xmlNodePtr cur)2242 xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur)
2243 {
2244 xmlOutputBufferPtr outbuf;
2245
2246 xmlInitParser();
2247
2248 if (cur == NULL) {
2249 #ifdef DEBUG_TREE
2250 xmlGenericError(xmlGenericErrorContext,
2251 "xmlElemDump : cur == NULL\n");
2252 #endif
2253 return;
2254 }
2255 #ifdef DEBUG_TREE
2256 if (doc == NULL) {
2257 xmlGenericError(xmlGenericErrorContext,
2258 "xmlElemDump : doc == NULL\n");
2259 }
2260 #endif
2261
2262 outbuf = xmlOutputBufferCreateFile(f, NULL);
2263 if (outbuf == NULL)
2264 return;
2265 if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) {
2266 #ifdef LIBXML_HTML_ENABLED
2267 htmlNodeDumpOutput(outbuf, doc, cur, NULL);
2268 #else
2269 xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n");
2270 #endif /* LIBXML_HTML_ENABLED */
2271 } else
2272 xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL);
2273 xmlOutputBufferClose(outbuf);
2274 }
2275
2276 /************************************************************************
2277 * *
2278 * Saving functions front-ends *
2279 * *
2280 ************************************************************************/
2281
2282 /**
2283 * xmlNodeDumpOutput:
2284 * @buf: the XML buffer output
2285 * @doc: the document
2286 * @cur: the current node
2287 * @level: the imbrication level for indenting
2288 * @format: is formatting allowed
2289 * @encoding: an optional encoding string
2290 *
2291 * Dump an XML node, recursive behaviour, children are printed too.
2292 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2293 * or xmlKeepBlanksDefault(0) was called
2294 */
2295 void
xmlNodeDumpOutput(xmlOutputBufferPtr buf,xmlDocPtr doc,xmlNodePtr cur,int level,int format,const char * encoding)2296 xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur,
2297 int level, int format, const char *encoding)
2298 {
2299 xmlSaveCtxt ctxt;
2300 #ifdef LIBXML_HTML_ENABLED
2301 xmlDtdPtr dtd;
2302 int is_xhtml = 0;
2303 #endif
2304
2305 xmlInitParser();
2306
2307 if ((buf == NULL) || (cur == NULL)) return;
2308
2309 if (encoding == NULL)
2310 encoding = "UTF-8";
2311
2312 memset(&ctxt, 0, sizeof(ctxt));
2313 ctxt.buf = buf;
2314 ctxt.level = level;
2315 ctxt.format = format ? 1 : 0;
2316 ctxt.encoding = (const xmlChar *) encoding;
2317 xmlSaveCtxtInit(&ctxt);
2318 ctxt.options |= XML_SAVE_AS_XML;
2319
2320 #ifdef LIBXML_HTML_ENABLED
2321 dtd = xmlGetIntSubset(doc);
2322 if (dtd != NULL) {
2323 is_xhtml = xmlIsXHTML(dtd->SystemID, dtd->ExternalID);
2324 if (is_xhtml < 0)
2325 is_xhtml = 0;
2326 }
2327
2328 if (is_xhtml)
2329 xhtmlNodeDumpOutput(&ctxt, cur);
2330 else
2331 #endif
2332 xmlNodeDumpOutputInternal(&ctxt, cur);
2333 }
2334
2335 /**
2336 * xmlDocDumpFormatMemoryEnc:
2337 * @out_doc: Document to generate XML text from
2338 * @doc_txt_ptr: Memory pointer for allocated XML text
2339 * @doc_txt_len: Length of the generated XML text
2340 * @txt_encoding: Character encoding to use when generating XML text
2341 * @format: should formatting spaces been added
2342 *
2343 * Dump the current DOM tree into memory using the character encoding specified
2344 * by the caller. Note it is up to the caller of this function to free the
2345 * allocated memory with xmlFree().
2346 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2347 * or xmlKeepBlanksDefault(0) was called
2348 */
2349
2350 void
xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding,int format)2351 xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2352 int * doc_txt_len, const char * txt_encoding,
2353 int format) {
2354 xmlSaveCtxt ctxt;
2355 int dummy = 0;
2356 xmlOutputBufferPtr out_buff = NULL;
2357 xmlCharEncodingHandlerPtr conv_hdlr = NULL;
2358
2359 if (doc_txt_len == NULL) {
2360 doc_txt_len = &dummy; /* Continue, caller just won't get length */
2361 }
2362
2363 if (doc_txt_ptr == NULL) {
2364 *doc_txt_len = 0;
2365 return;
2366 }
2367
2368 *doc_txt_ptr = NULL;
2369 *doc_txt_len = 0;
2370
2371 if (out_doc == NULL) {
2372 /* No document, no output */
2373 return;
2374 }
2375
2376 /*
2377 * Validate the encoding value, if provided.
2378 * This logic is copied from xmlSaveFileEnc.
2379 */
2380
2381 if (txt_encoding == NULL)
2382 txt_encoding = (const char *) out_doc->encoding;
2383 if (txt_encoding != NULL) {
2384 conv_hdlr = xmlFindCharEncodingHandler(txt_encoding);
2385 if ( conv_hdlr == NULL ) {
2386 xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc,
2387 txt_encoding);
2388 return;
2389 }
2390 }
2391
2392 if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) {
2393 xmlSaveErrMemory("creating buffer");
2394 return;
2395 }
2396
2397 memset(&ctxt, 0, sizeof(ctxt));
2398 ctxt.buf = out_buff;
2399 ctxt.level = 0;
2400 ctxt.format = format ? 1 : 0;
2401 ctxt.encoding = (const xmlChar *) txt_encoding;
2402 xmlSaveCtxtInit(&ctxt);
2403 ctxt.options |= XML_SAVE_AS_XML;
2404 xmlDocContentDumpOutput(&ctxt, out_doc);
2405 xmlOutputBufferFlush(out_buff);
2406 if (out_buff->conv != NULL) {
2407 *doc_txt_len = xmlBufUse(out_buff->conv);
2408 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len);
2409 } else {
2410 *doc_txt_len = xmlBufUse(out_buff->buffer);
2411 *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len);
2412 }
2413 (void)xmlOutputBufferClose(out_buff);
2414
2415 if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) {
2416 *doc_txt_len = 0;
2417 xmlSaveErrMemory("creating output");
2418 }
2419
2420 return;
2421 }
2422
2423 /**
2424 * xmlDocDumpMemory:
2425 * @cur: the document
2426 * @mem: OUT: the memory pointer
2427 * @size: OUT: the memory length
2428 *
2429 * Dump an XML document in memory and return the #xmlChar * and it's size
2430 * in bytes. It's up to the caller to free the memory with xmlFree().
2431 * The resulting byte array is zero terminated, though the last 0 is not
2432 * included in the returned size.
2433 */
2434 void
xmlDocDumpMemory(xmlDocPtr cur,xmlChar ** mem,int * size)2435 xmlDocDumpMemory(xmlDocPtr cur, xmlChar**mem, int *size) {
2436 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, 0);
2437 }
2438
2439 /**
2440 * xmlDocDumpFormatMemory:
2441 * @cur: the document
2442 * @mem: OUT: the memory pointer
2443 * @size: OUT: the memory length
2444 * @format: should formatting spaces been added
2445 *
2446 *
2447 * Dump an XML document in memory and return the #xmlChar * and it's size.
2448 * It's up to the caller to free the memory with xmlFree().
2449 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2450 * or xmlKeepBlanksDefault(0) was called
2451 */
2452 void
xmlDocDumpFormatMemory(xmlDocPtr cur,xmlChar ** mem,int * size,int format)2453 xmlDocDumpFormatMemory(xmlDocPtr cur, xmlChar**mem, int *size, int format) {
2454 xmlDocDumpFormatMemoryEnc(cur, mem, size, NULL, format);
2455 }
2456
2457 /**
2458 * xmlDocDumpMemoryEnc:
2459 * @out_doc: Document to generate XML text from
2460 * @doc_txt_ptr: Memory pointer for allocated XML text
2461 * @doc_txt_len: Length of the generated XML text
2462 * @txt_encoding: Character encoding to use when generating XML text
2463 *
2464 * Dump the current DOM tree into memory using the character encoding specified
2465 * by the caller. Note it is up to the caller of this function to free the
2466 * allocated memory with xmlFree().
2467 */
2468
2469 void
xmlDocDumpMemoryEnc(xmlDocPtr out_doc,xmlChar ** doc_txt_ptr,int * doc_txt_len,const char * txt_encoding)2470 xmlDocDumpMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr,
2471 int * doc_txt_len, const char * txt_encoding) {
2472 xmlDocDumpFormatMemoryEnc(out_doc, doc_txt_ptr, doc_txt_len,
2473 txt_encoding, 0);
2474 }
2475
2476 /**
2477 * xmlDocFormatDump:
2478 * @f: the FILE*
2479 * @cur: the document
2480 * @format: should formatting spaces been added
2481 *
2482 * Dump an XML document to an open FILE.
2483 *
2484 * returns: the number of bytes written or -1 in case of failure.
2485 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2486 * or xmlKeepBlanksDefault(0) was called
2487 */
2488 int
xmlDocFormatDump(FILE * f,xmlDocPtr cur,int format)2489 xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) {
2490 xmlSaveCtxt ctxt;
2491 xmlOutputBufferPtr buf;
2492 const char * encoding;
2493 xmlCharEncodingHandlerPtr handler = NULL;
2494 int ret;
2495
2496 if (cur == NULL) {
2497 #ifdef DEBUG_TREE
2498 xmlGenericError(xmlGenericErrorContext,
2499 "xmlDocDump : document == NULL\n");
2500 #endif
2501 return(-1);
2502 }
2503 encoding = (const char *) cur->encoding;
2504
2505 if (encoding != NULL) {
2506 handler = xmlFindCharEncodingHandler(encoding);
2507 if (handler == NULL) {
2508 xmlFree((char *) cur->encoding);
2509 cur->encoding = NULL;
2510 encoding = NULL;
2511 }
2512 }
2513 buf = xmlOutputBufferCreateFile(f, handler);
2514 if (buf == NULL) return(-1);
2515 memset(&ctxt, 0, sizeof(ctxt));
2516 ctxt.buf = buf;
2517 ctxt.level = 0;
2518 ctxt.format = format ? 1 : 0;
2519 ctxt.encoding = (const xmlChar *) encoding;
2520 xmlSaveCtxtInit(&ctxt);
2521 ctxt.options |= XML_SAVE_AS_XML;
2522 xmlDocContentDumpOutput(&ctxt, cur);
2523
2524 ret = xmlOutputBufferClose(buf);
2525 return(ret);
2526 }
2527
2528 /**
2529 * xmlDocDump:
2530 * @f: the FILE*
2531 * @cur: the document
2532 *
2533 * Dump an XML document to an open FILE.
2534 *
2535 * returns: the number of bytes written or -1 in case of failure.
2536 */
2537 int
xmlDocDump(FILE * f,xmlDocPtr cur)2538 xmlDocDump(FILE *f, xmlDocPtr cur) {
2539 return(xmlDocFormatDump (f, cur, 0));
2540 }
2541
2542 /**
2543 * xmlSaveFileTo:
2544 * @buf: an output I/O buffer
2545 * @cur: the document
2546 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2547 *
2548 * Dump an XML document to an I/O buffer.
2549 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2550 * after this call.
2551 *
2552 * returns: the number of bytes written or -1 in case of failure.
2553 */
2554 int
xmlSaveFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding)2555 xmlSaveFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur, const char *encoding) {
2556 xmlSaveCtxt ctxt;
2557 int ret;
2558
2559 if (buf == NULL) return(-1);
2560 if (cur == NULL) {
2561 xmlOutputBufferClose(buf);
2562 return(-1);
2563 }
2564 memset(&ctxt, 0, sizeof(ctxt));
2565 ctxt.buf = buf;
2566 ctxt.level = 0;
2567 ctxt.format = 0;
2568 ctxt.encoding = (const xmlChar *) encoding;
2569 xmlSaveCtxtInit(&ctxt);
2570 ctxt.options |= XML_SAVE_AS_XML;
2571 xmlDocContentDumpOutput(&ctxt, cur);
2572 ret = xmlOutputBufferClose(buf);
2573 return(ret);
2574 }
2575
2576 /**
2577 * xmlSaveFormatFileTo:
2578 * @buf: an output I/O buffer
2579 * @cur: the document
2580 * @encoding: the encoding if any assuming the I/O layer handles the transcoding
2581 * @format: should formatting spaces been added
2582 *
2583 * Dump an XML document to an I/O buffer.
2584 * Warning ! This call xmlOutputBufferClose() on buf which is not available
2585 * after this call.
2586 *
2587 * returns: the number of bytes written or -1 in case of failure.
2588 */
2589 int
xmlSaveFormatFileTo(xmlOutputBufferPtr buf,xmlDocPtr cur,const char * encoding,int format)2590 xmlSaveFormatFileTo(xmlOutputBufferPtr buf, xmlDocPtr cur,
2591 const char *encoding, int format)
2592 {
2593 xmlSaveCtxt ctxt;
2594 int ret;
2595
2596 if (buf == NULL) return(-1);
2597 if ((cur == NULL) ||
2598 ((cur->type != XML_DOCUMENT_NODE) &&
2599 (cur->type != XML_HTML_DOCUMENT_NODE))) {
2600 xmlOutputBufferClose(buf);
2601 return(-1);
2602 }
2603 memset(&ctxt, 0, sizeof(ctxt));
2604 ctxt.buf = buf;
2605 ctxt.level = 0;
2606 ctxt.format = format ? 1 : 0;
2607 ctxt.encoding = (const xmlChar *) encoding;
2608 xmlSaveCtxtInit(&ctxt);
2609 ctxt.options |= XML_SAVE_AS_XML;
2610 xmlDocContentDumpOutput(&ctxt, cur);
2611 ret = xmlOutputBufferClose(buf);
2612 return (ret);
2613 }
2614
2615 /**
2616 * xmlSaveFormatFileEnc:
2617 * @filename: the filename or URL to output
2618 * @cur: the document being saved
2619 * @encoding: the name of the encoding to use or NULL.
2620 * @format: should formatting spaces be added.
2621 *
2622 * Dump an XML document to a file or an URL.
2623 *
2624 * Returns the number of bytes written or -1 in case of error.
2625 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2626 * or xmlKeepBlanksDefault(0) was called
2627 */
2628 int
xmlSaveFormatFileEnc(const char * filename,xmlDocPtr cur,const char * encoding,int format)2629 xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur,
2630 const char * encoding, int format ) {
2631 xmlSaveCtxt ctxt;
2632 xmlOutputBufferPtr buf;
2633 xmlCharEncodingHandlerPtr handler = NULL;
2634 int ret;
2635
2636 if (cur == NULL)
2637 return(-1);
2638
2639 if (encoding == NULL)
2640 encoding = (const char *) cur->encoding;
2641
2642 if (encoding != NULL) {
2643
2644 handler = xmlFindCharEncodingHandler(encoding);
2645 if (handler == NULL)
2646 return(-1);
2647 }
2648
2649 #ifdef LIBXML_ZLIB_ENABLED
2650 if (cur->compression < 0) cur->compression = xmlGetCompressMode();
2651 #endif
2652 /*
2653 * save the content to a temp buffer.
2654 */
2655 buf = xmlOutputBufferCreateFilename(filename, handler, cur->compression);
2656 if (buf == NULL) return(-1);
2657 memset(&ctxt, 0, sizeof(ctxt));
2658 ctxt.buf = buf;
2659 ctxt.level = 0;
2660 ctxt.format = format ? 1 : 0;
2661 ctxt.encoding = (const xmlChar *) encoding;
2662 xmlSaveCtxtInit(&ctxt);
2663 ctxt.options |= XML_SAVE_AS_XML;
2664
2665 xmlDocContentDumpOutput(&ctxt, cur);
2666
2667 ret = xmlOutputBufferClose(buf);
2668 return(ret);
2669 }
2670
2671
2672 /**
2673 * xmlSaveFileEnc:
2674 * @filename: the filename (or URL)
2675 * @cur: the document
2676 * @encoding: the name of an encoding (or NULL)
2677 *
2678 * Dump an XML document, converting it to the given encoding
2679 *
2680 * returns: the number of bytes written or -1 in case of failure.
2681 */
2682 int
xmlSaveFileEnc(const char * filename,xmlDocPtr cur,const char * encoding)2683 xmlSaveFileEnc(const char *filename, xmlDocPtr cur, const char *encoding) {
2684 return ( xmlSaveFormatFileEnc( filename, cur, encoding, 0 ) );
2685 }
2686
2687 /**
2688 * xmlSaveFormatFile:
2689 * @filename: the filename (or URL)
2690 * @cur: the document
2691 * @format: should formatting spaces been added
2692 *
2693 * Dump an XML document to a file. Will use compression if
2694 * compiled in and enabled. If @filename is "-" the stdout file is
2695 * used. If @format is set then the document will be indented on output.
2696 * Note that @format = 1 provide node indenting only if xmlIndentTreeOutput = 1
2697 * or xmlKeepBlanksDefault(0) was called
2698 *
2699 * returns: the number of bytes written or -1 in case of failure.
2700 */
2701 int
xmlSaveFormatFile(const char * filename,xmlDocPtr cur,int format)2702 xmlSaveFormatFile(const char *filename, xmlDocPtr cur, int format) {
2703 return ( xmlSaveFormatFileEnc( filename, cur, NULL, format ) );
2704 }
2705
2706 /**
2707 * xmlSaveFile:
2708 * @filename: the filename (or URL)
2709 * @cur: the document
2710 *
2711 * Dump an XML document to a file. Will use compression if
2712 * compiled in and enabled. If @filename is "-" the stdout file is
2713 * used.
2714 * returns: the number of bytes written or -1 in case of failure.
2715 */
2716 int
xmlSaveFile(const char * filename,xmlDocPtr cur)2717 xmlSaveFile(const char *filename, xmlDocPtr cur) {
2718 return(xmlSaveFormatFileEnc(filename, cur, NULL, 0));
2719 }
2720
2721 #endif /* LIBXML_OUTPUT_ENABLED */
2722
2723 #define bottom_xmlsave
2724 #include "elfgcchack.h"
2725