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