/*------------------------------------------------------------------------- * drawElements Quality Program Helper Library * ------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief XML Writer. *//*--------------------------------------------------------------------*/ #include "qpXmlWriter.h" #include "deMemory.h" #include "deInt32.h" /*------------------------------------------------------------------------ * qpXmlWriter stand-alone implementation. *----------------------------------------------------------------------*/ #include "deMemPool.h" #include "dePoolArray.h" struct qpXmlWriter_s { FILE* outputFile; deBool flushAfterWrite; deBool xmlPrevIsStartElement; deBool xmlIsWriting; int xmlElementDepth; }; static deBool writeEscaped (qpXmlWriter* writer, const char* str) { char buf[256 + 10]; char* d = &buf[0]; const char* s = str; deBool isEOS = DE_FALSE; do { /* Check for characters that need to be escaped. */ const char* repl = DE_NULL; switch (*s) { case 0: isEOS = DE_TRUE; break; case '<': repl = "<"; break; case '>': repl = ">"; break; case '&': repl = "&"; break; case '\'': repl = "'"; break; case '"': repl = """; break; /* Non-printable characters. */ case 1: repl = "<SOH>"; break; case 2: repl = "<STX>"; break; case 3: repl = "<ETX>"; break; case 4: repl = "<EOT>"; break; case 5: repl = "<ENQ>"; break; case 6: repl = "<ACK>"; break; case 7: repl = "<BEL>"; break; case 8: repl = "<BS>"; break; case 11: repl = "<VT>"; break; case 12: repl = "<FF>"; break; case 14: repl = "<SO>"; break; case 15: repl = "<SI>"; break; case 16: repl = "<DLE>"; break; case 17: repl = "<DC1>"; break; case 18: repl = "<DC2>"; break; case 19: repl = "<DC3>"; break; case 20: repl = "<DC4>"; break; case 21: repl = "<NAK>"; break; case 22: repl = "<SYN>"; break; case 23: repl = "<ETB>"; break; case 24: repl = "<CAN>"; break; case 25: repl = "<EM>"; break; case 26: repl = "<SUB>"; break; case 27: repl = "<ESC>"; break; case 28: repl = "<FS>"; break; case 29: repl = "<GS>"; break; case 30: repl = "<RS>"; break; case 31: repl = "<US>"; break; default: /* nada */ break; } /* Write out char or escape sequence. */ if (repl) { s++; strcpy(d, repl); d += strlen(repl); } else *d++ = *s++; /* Write buffer if EOS or buffer full. */ if (isEOS || ((d - &buf[0]) >= 4)) { *d = 0; fprintf(writer->outputFile, "%s", buf); d = &buf[0]; } } while (!isEOS); if (writer->flushAfterWrite) fflush(writer->outputFile); DE_ASSERT(d == &buf[0]); /* buffer must be empty */ return DE_TRUE; } qpXmlWriter* qpXmlWriter_createFileWriter (FILE* outputFile, deBool useCompression, deBool flushAfterWrite) { qpXmlWriter* writer = (qpXmlWriter*)deCalloc(sizeof(qpXmlWriter)); if (!writer) return DE_NULL; DE_UNREF(useCompression); /* no compression supported. */ writer->outputFile = outputFile; writer->flushAfterWrite = flushAfterWrite; return writer; } void qpXmlWriter_destroy (qpXmlWriter* writer) { DE_ASSERT(writer); deFree(writer); } static deBool closePending (qpXmlWriter* writer) { if (writer->xmlPrevIsStartElement) { fprintf(writer->outputFile, ">\n"); writer->xmlPrevIsStartElement = DE_FALSE; } return DE_TRUE; } void qpXmlWriter_flush (qpXmlWriter* writer) { closePending(writer); } deBool qpXmlWriter_startDocument (qpXmlWriter* writer) { DE_ASSERT(writer && !writer->xmlIsWriting); writer->xmlIsWriting = DE_TRUE; writer->xmlElementDepth = 0; writer->xmlPrevIsStartElement = DE_FALSE; fprintf(writer->outputFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); return DE_TRUE; } static const char* getIndentStr (int indentLevel) { static const char s_indentStr[33] = " "; static const int s_indentStrLen = 32; return &s_indentStr[s_indentStrLen - deMin32(s_indentStrLen, indentLevel)]; } deBool qpXmlWriter_endDocument (qpXmlWriter* writer) { DE_ASSERT(writer); DE_ASSERT(writer->xmlIsWriting); DE_ASSERT(writer->xmlElementDepth == 0); closePending(writer); writer->xmlIsWriting = DE_FALSE; return DE_TRUE; } deBool qpXmlWriter_writeString (qpXmlWriter* writer, const char* str) { if (writer->xmlPrevIsStartElement) { fprintf(writer->outputFile, ">"); writer->xmlPrevIsStartElement = DE_FALSE; } return writeEscaped(writer, str); } deBool qpXmlWriter_startElement(qpXmlWriter* writer, const char* elementName, int numAttribs, const qpXmlAttribute* attribs) { int ndx; closePending(writer); fprintf(writer->outputFile, "%s<%s", getIndentStr(writer->xmlElementDepth), elementName); for (ndx = 0; ndx < numAttribs; ndx++) { const qpXmlAttribute* attrib = &attribs[ndx]; fprintf(writer->outputFile, " %s=\"", attrib->name); switch (attrib->type) { case QP_XML_ATTRIBUTE_STRING: writeEscaped(writer, attrib->stringValue); break; case QP_XML_ATTRIBUTE_INT: { char buf[64]; sprintf(buf, "%d", attrib->intValue); writeEscaped(writer, buf); break; } case QP_XML_ATTRIBUTE_BOOL: writeEscaped(writer, attrib->boolValue ? "True" : "False"); break; default: DE_ASSERT(DE_FALSE); } fprintf(writer->outputFile, "\""); } writer->xmlElementDepth++; writer->xmlPrevIsStartElement = DE_TRUE; return DE_TRUE; } deBool qpXmlWriter_endElement (qpXmlWriter* writer, const char* elementName) { DE_ASSERT(writer && writer->xmlElementDepth > 0); writer->xmlElementDepth--; if (writer->xmlPrevIsStartElement) /* leave flag as-is */ { fprintf(writer->outputFile, " />\n"); writer->xmlPrevIsStartElement = DE_FALSE; } else fprintf(writer->outputFile, "</%s>\n", /*getIndentStr(writer->xmlElementDepth),*/ elementName); return DE_TRUE; } deBool qpXmlWriter_writeBase64 (qpXmlWriter* writer, const deUint8* data, size_t numBytes) { static const char s_base64Table[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+','/' }; int numWritten = 0; size_t srcNdx = 0; deBool writeIndent = DE_TRUE; const char* indentStr = getIndentStr(writer->xmlElementDepth); DE_ASSERT(writer && data && (numBytes > 0)); /* Close and pending writes. */ closePending(writer); /* Loop all input chars. */ while (srcNdx < numBytes) { size_t numRead = (size_t)deMin32(3, (int)(numBytes - srcNdx)); deUint8 s0 = data[srcNdx]; deUint8 s1 = (numRead >= 2) ? data[srcNdx+1] : 0; deUint8 s2 = (numRead >= 3) ? data[srcNdx+2] : 0; char d[5]; srcNdx += numRead; d[0] = s_base64Table[s0 >> 2]; d[1] = s_base64Table[((s0&0x3)<<4) | (s1>>4)]; d[2] = s_base64Table[((s1&0xF)<<2) | (s2>>6)]; d[3] = s_base64Table[s2&0x3F]; d[4] = 0; if (numRead < 3) d[3] = '='; if (numRead < 2) d[2] = '='; /* Write indent (if needed). */ if (writeIndent) { fprintf(writer->outputFile, "%s", indentStr); writeIndent = DE_FALSE; } /* Write data. */ fprintf(writer->outputFile, "%s", &d[0]); /* EOL every now and then. */ numWritten += 4; if (numWritten >= 64) { fprintf(writer->outputFile, "\n"); numWritten = 0; writeIndent = DE_TRUE; } } /* Last EOL. */ if (numWritten > 0) fprintf(writer->outputFile, "\n"); DE_ASSERT(srcNdx == numBytes); return DE_TRUE; } /* Common helper functions. */ deBool qpXmlWriter_writeStringElement (qpXmlWriter* writer, const char* elementName, const char* elementContent) { if (!qpXmlWriter_startElement(writer, elementName, 0, DE_NULL) || (elementContent && !qpXmlWriter_writeString(writer, elementContent)) || !qpXmlWriter_endElement(writer, elementName)) return DE_FALSE; return DE_TRUE; }