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 = "<"; break;
60 case '>': repl = ">"; break;
61 case '&': repl = "&"; break;
62 case '\'': repl = "'"; break;
63 case '"': repl = """; break;
64
65 /* Non-printable characters. */
66 case 1: repl = "<SOH>"; break;
67 case 2: repl = "<STX>"; break;
68 case 3: repl = "<ETX>"; break;
69 case 4: repl = "<EOT>"; break;
70 case 5: repl = "<ENQ>"; break;
71 case 6: repl = "<ACK>"; break;
72 case 7: repl = "<BEL>"; break;
73 case 8: repl = "<BS>"; break;
74 case 11: repl = "<VT>"; break;
75 case 12: repl = "<FF>"; break;
76 case 14: repl = "<SO>"; break;
77 case 15: repl = "<SI>"; break;
78 case 16: repl = "<DLE>"; break;
79 case 17: repl = "<DC1>"; break;
80 case 18: repl = "<DC2>"; break;
81 case 19: repl = "<DC3>"; break;
82 case 20: repl = "<DC4>"; break;
83 case 21: repl = "<NAK>"; break;
84 case 22: repl = "<SYN>"; break;
85 case 23: repl = "<ETB>"; break;
86 case 24: repl = "<CAN>"; break;
87 case 25: repl = "<EM>"; break;
88 case 26: repl = "<SUB>"; break;
89 case 27: repl = "<ESC>"; break;
90 case 28: repl = "<FS>"; break;
91 case 29: repl = "<GS>"; break;
92 case 30: repl = "<RS>"; break;
93 case 31: repl = "<US>"; 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