File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / xmlsave.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 19:53:29 2014 UTC (9 years, 11 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_9_1p0, v2_9_1, HEAD
libxml2 2.9.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>