1 
2 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
3 // Access Protocol.
4 
5 // Copyright (c) 2010 OPeNDAP, Inc.
6 // Author: James Gallagher <jgallagher@opendap.org>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 //
22 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
23 
24 /*
25  * XMLWriter.cpp
26  *
27  *  Created on: Jul 28, 2010
28  *      Author: jimg
29  */
30 
31 #include "config.h"
32 
33 #include <libxml/encoding.h>
34 #include <libxml/xmlwriter.h>
35 
36 #include "XMLWriter.h"
37 #include "InternalErr.h"
38 
39 // TODO - Bite the bullet and make the encoding UTF-8 as required by dap4. This will break a lot of tests but the baselines could be amended using  a bash script and sed.
40 const char *ENCODING = "ISO-8859-1";
41 const int XML_BUF_SIZE = 2000000;
42 
43 using namespace libdap;
44 
XMLWriter(const string & pad)45 XMLWriter::XMLWriter(const string &pad) {
46     // LEAK The LIBXML_TEST_VERSION macro leaks 40 bytes according to valgrind
47     // on centos7. jhrg 6/19/19
48     // LIBXML_TEST_VERSION;
49 
50     /* Create a new XML buffer, to which the XML document will be
51      * written */
52     try {
53         if (!(d_doc_buf = xmlBufferCreateSize(XML_BUF_SIZE)))
54             throw InternalErr(__FILE__, __LINE__, "Error allocating the xml buffer");
55 
56         xmlBufferSetAllocationScheme(d_doc_buf, XML_BUFFER_ALLOC_DOUBLEIT);
57 
58         /* Create a new XmlWriter for memory, with no compression.
59          * Remark: there is no compression for this kind of xmlTextWriter */
60         if (!(d_writer = xmlNewTextWriterMemory(d_doc_buf, 0)))
61             throw InternalErr(__FILE__, __LINE__, "Error allocating memory for xml writer");
62 
63         if (xmlTextWriterSetIndent(d_writer, pad.length()) < 0)
64             throw InternalErr(__FILE__, __LINE__, "Error starting indentation for response document ");
65 
66         if (xmlTextWriterSetIndentString(d_writer, (const xmlChar*)pad.c_str()) < 0)
67             throw InternalErr(__FILE__, __LINE__, "Error setting indentation for response document ");
68 
69         d_started = true;
70         d_ended = false;
71 
72         /* Start the document with the xml default for the version,
73          * encoding ISO 8859-1 and the default for the standalone
74          * declaration. MY_ENCODING defined at top of this file*/
75         if (xmlTextWriterStartDocument(d_writer, NULL, ENCODING, NULL) < 0)
76             throw InternalErr(__FILE__, __LINE__, "Error starting xml response document");
77     }
78     catch (InternalErr &e) {
79         m_cleanup();
80         throw;
81     }
82 
83 }
84 
~XMLWriter()85 XMLWriter::~XMLWriter() {
86     m_cleanup();
87 }
88 
m_cleanup()89 void XMLWriter::m_cleanup() {
90     // make sure the buffer and writer are all cleaned up
91     if (d_writer) {
92         xmlFreeTextWriter(d_writer); // This frees both d_writer and d_doc_buf
93         d_writer = 0;
94         // d_doc_buf = 0;
95     }
96 
97     // We could be here because of an exception and d_writer might be zero
98     if (d_doc_buf) {
99         xmlBufferFree(d_doc_buf);
100         d_doc_buf = 0;
101     }
102 
103     d_started = false;
104     d_ended = false;
105 }
106 
get_doc()107 const char *XMLWriter::get_doc() {
108     if (d_writer && d_started) {
109         if (xmlTextWriterEndDocument(d_writer) < 0)
110             throw InternalErr(__FILE__, __LINE__, "Error ending the document");
111 
112         d_ended = true;
113 
114         // must call this before getting the buffer content. Odd, but appears to be true.
115         // jhrg
116         xmlFreeTextWriter(d_writer);
117         d_writer = 0;
118     }
119 
120     if (!d_doc_buf->content)
121         throw InternalErr(__FILE__, __LINE__, "Error retrieving response document as string");
122 
123     return (const char *)d_doc_buf->content;
124 }
125 
get_doc_size()126 unsigned int XMLWriter::get_doc_size() {
127     if (d_writer && d_started) {
128         if (xmlTextWriterEndDocument(d_writer) < 0)
129             throw InternalErr(__FILE__, __LINE__, "Error ending the document");
130 
131         d_ended = true;
132 
133         // must call this before getting the buffer content. Odd, but appears to be true.
134         // jhrg
135         xmlFreeTextWriter(d_writer);
136         d_writer = 0;
137     }
138 
139     if (!d_doc_buf->content)
140         throw InternalErr(__FILE__, __LINE__, "Error retrieving response document as string");
141 
142     // how much of the buffer is in use?
143     return d_doc_buf->use;
144 }
145