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