1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Helper Library
3  * -------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief XML Writer.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "qpXmlWriter.h"
25 
26 #include "deMemory.h"
27 #include "deInt32.h"
28 
29 /*------------------------------------------------------------------------
30  * qpXmlWriter stand-alone implementation.
31  *----------------------------------------------------------------------*/
32 
33 #include "deMemPool.h"
34 #include "dePoolArray.h"
35 
36 struct qpXmlWriter_s
37 {
38 	FILE*				outputFile;
39 	deBool				flushAfterWrite;
40 
41 	deBool				xmlPrevIsStartElement;
42 	deBool				xmlIsWriting;
43 	int					xmlElementDepth;
44 };
45 
writeEscaped(qpXmlWriter * writer,const char * str)46 static deBool writeEscaped (qpXmlWriter* writer, const char* str)
47 {
48 	char		buf[256 + 10];
49 	char*		d		= &buf[0];
50 	const char*	s		= str;
51 	deBool		isEOS	= DE_FALSE;
52 
53 	do
54 	{
55 		/* Check for characters that need to be escaped. */
56 		const char* repl = DE_NULL;
57 		switch (*s)
58 		{
59 			case 0:		isEOS = DE_TRUE;		break;
60 			case '<':	repl = "&lt;";			break;
61 			case '>':	repl = "&gt;";			break;
62 			case '&':	repl = "&amp;";			break;
63 			case '\'':	repl = "&apos;";		break;
64 			case '"':	repl = "&quot;";		break;
65 
66 			/* Non-printable characters. */
67 			case 1:		repl = "&lt;SOH&gt;";	break;
68 			case 2:		repl = "&lt;STX&gt;";	break;
69 			case 3:		repl = "&lt;ETX&gt;";	break;
70 			case 4:		repl = "&lt;EOT&gt;";	break;
71 			case 5:		repl = "&lt;ENQ&gt;";	break;
72 			case 6:		repl = "&lt;ACK&gt;";	break;
73 			case 7:		repl = "&lt;BEL&gt;";	break;
74 			case 8:		repl = "&lt;BS&gt;";	break;
75 			case 11:	repl = "&lt;VT&gt;";	break;
76 			case 12:	repl = "&lt;FF&gt;";	break;
77 			case 14:	repl = "&lt;SO&gt;";	break;
78 			case 15:	repl = "&lt;SI&gt;";	break;
79 			case 16:	repl = "&lt;DLE&gt;";	break;
80 			case 17:	repl = "&lt;DC1&gt;";	break;
81 			case 18:	repl = "&lt;DC2&gt;";	break;
82 			case 19:	repl = "&lt;DC3&gt;";	break;
83 			case 20:	repl = "&lt;DC4&gt;";	break;
84 			case 21:	repl = "&lt;NAK&gt;";	break;
85 			case 22:	repl = "&lt;SYN&gt;";	break;
86 			case 23:	repl = "&lt;ETB&gt;";	break;
87 			case 24:	repl = "&lt;CAN&gt;";	break;
88 			case 25:	repl = "&lt;EM&gt;";	break;
89 			case 26:	repl = "&lt;SUB&gt;";	break;
90 			case 27:	repl = "&lt;ESC&gt;";	break;
91 			case 28:	repl = "&lt;FS&gt;";	break;
92 			case 29:	repl = "&lt;GS&gt;";	break;
93 			case 30:	repl = "&lt;RS&gt;";	break;
94 			case 31:	repl = "&lt;US&gt;";	break;
95 
96 			default:	/* nada */				break;
97 		}
98 
99 		/* Write out char or escape sequence. */
100 		if (repl)
101 		{
102 			s++;
103 			strcpy(d, repl);
104 			d += strlen(repl);
105 		}
106 		else
107 			*d++ = *s++;
108 
109 		/* Write buffer if EOS or buffer full. */
110 		if (isEOS || ((d - &buf[0]) >= 4))
111 		{
112 			*d = 0;
113 			fprintf(writer->outputFile, "%s", buf);
114 			d = &buf[0];
115 		}
116 	} while (!isEOS);
117 
118 	if (writer->flushAfterWrite)
119 		fflush(writer->outputFile);
120 	DE_ASSERT(d == &buf[0]); /* buffer must be empty */
121 	return DE_TRUE;
122 }
123 
qpXmlWriter_createFileWriter(FILE * outputFile,deBool useCompression,deBool flushAfterWrite)124 qpXmlWriter* qpXmlWriter_createFileWriter (FILE* outputFile, deBool useCompression, deBool flushAfterWrite)
125 {
126 	qpXmlWriter* writer = (qpXmlWriter*)deCalloc(sizeof(qpXmlWriter));
127 	if (!writer)
128 		return DE_NULL;
129 
130 	DE_UNREF(useCompression); /* no compression supported. */
131 
132 	writer->outputFile = outputFile;
133 	writer->flushAfterWrite = flushAfterWrite;
134 
135 	return writer;
136 }
137 
qpXmlWriter_destroy(qpXmlWriter * writer)138 void qpXmlWriter_destroy (qpXmlWriter* writer)
139 {
140 	DE_ASSERT(writer);
141 
142 	deFree(writer);
143 }
144 
closePending(qpXmlWriter * writer)145 static deBool closePending (qpXmlWriter* writer)
146 {
147 	if (writer->xmlPrevIsStartElement)
148 	{
149 		fprintf(writer->outputFile, ">\n");
150 		writer->xmlPrevIsStartElement = DE_FALSE;
151 	}
152 
153 	return DE_TRUE;
154 }
155 
qpXmlWriter_flush(qpXmlWriter * writer)156 void qpXmlWriter_flush (qpXmlWriter* writer)
157 {
158 	closePending(writer);
159 }
160 
qpXmlWriter_startDocument(qpXmlWriter * writer)161 deBool qpXmlWriter_startDocument (qpXmlWriter* writer)
162 {
163 	DE_ASSERT(writer && !writer->xmlIsWriting);
164 	writer->xmlIsWriting			= DE_TRUE;
165 	writer->xmlElementDepth			= 0;
166 	writer->xmlPrevIsStartElement	= DE_FALSE;
167 	fprintf(writer->outputFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
168 	return DE_TRUE;
169 }
170 
getIndentStr(int indentLevel)171 static const char* getIndentStr (int indentLevel)
172 {
173 	static const char	s_indentStr[33]	= "                                ";
174 	static const int	s_indentStrLen	= 32;
175 	return &s_indentStr[s_indentStrLen - deMin32(s_indentStrLen, indentLevel)];
176 }
177 
qpXmlWriter_endDocument(qpXmlWriter * writer)178 deBool qpXmlWriter_endDocument (qpXmlWriter* writer)
179 {
180 	DE_ASSERT(writer);
181 	DE_ASSERT(writer->xmlIsWriting);
182 	DE_ASSERT(writer->xmlElementDepth == 0);
183 	closePending(writer);
184 	writer->xmlIsWriting = DE_FALSE;
185 	return DE_TRUE;
186 }
187 
qpXmlWriter_writeString(qpXmlWriter * writer,const char * str)188 deBool qpXmlWriter_writeString (qpXmlWriter* writer, const char* str)
189 {
190 	if (writer->xmlPrevIsStartElement)
191 	{
192 		fprintf(writer->outputFile, ">");
193 		writer->xmlPrevIsStartElement = DE_FALSE;
194 	}
195 
196 	return writeEscaped(writer, str);
197 }
198 
qpXmlWriter_startElement(qpXmlWriter * writer,const char * elementName,int numAttribs,const qpXmlAttribute * attribs)199 deBool qpXmlWriter_startElement(qpXmlWriter* writer, const char* elementName, int numAttribs, const qpXmlAttribute* attribs)
200 {
201 	int ndx;
202 
203 	closePending(writer);
204 
205 	fprintf(writer->outputFile, "%s<%s", getIndentStr(writer->xmlElementDepth), elementName);
206 
207 	for (ndx = 0; ndx < numAttribs; ndx++)
208 	{
209 		const qpXmlAttribute* attrib = &attribs[ndx];
210 		fprintf(writer->outputFile, " %s=\"", attrib->name);
211 		switch (attrib->type)
212 		{
213 			case QP_XML_ATTRIBUTE_STRING:
214 				writeEscaped(writer, attrib->stringValue);
215 				break;
216 
217 			case QP_XML_ATTRIBUTE_INT:
218 			{
219 				char buf[64];
220 				sprintf(buf, "%d", attrib->intValue);
221 				writeEscaped(writer, buf);
222 				break;
223 			}
224 
225 			case QP_XML_ATTRIBUTE_BOOL:
226 				writeEscaped(writer, attrib->boolValue ? "True" : "False");
227 				break;
228 
229 			default:
230 				DE_ASSERT(DE_FALSE);
231 		}
232 		fprintf(writer->outputFile, "\"");
233 	}
234 
235 	writer->xmlElementDepth++;
236 	writer->xmlPrevIsStartElement = DE_TRUE;
237 	return DE_TRUE;
238 }
239 
qpXmlWriter_endElement(qpXmlWriter * writer,const char * elementName)240 deBool qpXmlWriter_endElement (qpXmlWriter* writer, const char* elementName)
241 {
242 	DE_ASSERT(writer && writer->xmlElementDepth > 0);
243 	writer->xmlElementDepth--;
244 
245 	if (writer->xmlPrevIsStartElement) /* leave flag as-is */
246 	{
247 		fprintf(writer->outputFile, " />\n");
248 		writer->xmlPrevIsStartElement = DE_FALSE;
249 	}
250 	else
251 		fprintf(writer->outputFile, "</%s>\n", /*getIndentStr(writer->xmlElementDepth),*/ elementName);
252 
253 	return DE_TRUE;
254 }
255 
qpXmlWriter_writeBase64(qpXmlWriter * writer,const deUint8 * data,size_t numBytes)256 deBool qpXmlWriter_writeBase64 (qpXmlWriter* writer, const deUint8* data, size_t numBytes)
257 {
258 	static const char s_base64Table[64] =
259 	{
260 		'A','B','C','D','E','F','G','H','I','J','K','L','M',
261 		'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
262 		'a','b','c','d','e','f','g','h','i','j','k','l','m',
263 		'n','o','p','q','r','s','t','u','v','w','x','y','z',
264 		'0','1','2','3','4','5','6','7','8','9','+','/'
265 	};
266 
267 	int			numWritten	= 0;
268 	size_t		srcNdx		= 0;
269 	deBool		writeIndent	= DE_TRUE;
270 	const char*	indentStr	= getIndentStr(writer->xmlElementDepth);
271 
272 	DE_ASSERT(writer && data && (numBytes > 0));
273 
274 	/* Close and pending writes. */
275 	closePending(writer);
276 
277 	/* Loop all input chars. */
278 	while (srcNdx < numBytes)
279 	{
280 		size_t	numRead = (size_t)deMin32(3, (int)(numBytes - srcNdx));
281 		deUint8	s0 = data[srcNdx];
282 		deUint8	s1 = (numRead >= 2) ? data[srcNdx+1] : 0;
283 		deUint8	s2 = (numRead >= 3) ? data[srcNdx+2] : 0;
284 		char	d[5];
285 
286 		srcNdx += numRead;
287 
288 		d[0] = s_base64Table[s0 >> 2];
289 		d[1] = s_base64Table[((s0&0x3)<<4) | (s1>>4)];
290 		d[2] = s_base64Table[((s1&0xF)<<2) | (s2>>6)];
291 		d[3] = s_base64Table[s2&0x3F];
292 		d[4] = 0;
293 
294 		if (numRead < 3) d[3] = '=';
295 		if (numRead < 2) d[2] = '=';
296 
297 		/* Write indent (if needed). */
298 		if (writeIndent)
299 		{
300 			fprintf(writer->outputFile, "%s", indentStr);
301 			writeIndent = DE_FALSE;
302 		}
303 
304 		/* Write data. */
305 		fprintf(writer->outputFile, "%s", &d[0]);
306 
307 		/* EOL every now and then. */
308 		numWritten += 4;
309 		if (numWritten >= 64)
310 		{
311 			fprintf(writer->outputFile, "\n");
312 			numWritten = 0;
313 			writeIndent = DE_TRUE;
314 		}
315 	}
316 
317 	/* Last EOL. */
318 	if (numWritten > 0)
319 		fprintf(writer->outputFile, "\n");
320 
321 	DE_ASSERT(srcNdx == numBytes);
322 	return DE_TRUE;
323 }
324 
325 /* Common helper functions. */
326 
qpXmlWriter_writeStringElement(qpXmlWriter * writer,const char * elementName,const char * elementContent)327 deBool qpXmlWriter_writeStringElement (qpXmlWriter* writer, const char* elementName, const char* elementContent)
328 {
329 	if (!qpXmlWriter_startElement(writer, elementName, 0, DE_NULL) ||
330 		(elementContent && !qpXmlWriter_writeString(writer, elementContent)) ||
331 		!qpXmlWriter_endElement(writer, elementName))
332 		return DE_FALSE;
333 
334 	return DE_TRUE;
335 }
336