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