Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

XMLLoadSave.cc

Go to the documentation of this file.
00001 #include "XMLLoadSave.h"
00002 #include <iostream>
00003 #include <string>
00004 #include <libxml/xmlmemory.h>
00005 #include <libxml/parser.h>
00006 #include <libxml/parserInternals.h>
00007 #include <libxml/xmlsave.h>
00008 #include <libxml/xmlversion.h>
00009 #include <errno.h>
00010 
00011 #if LIBXML_VERSION < 20617
00012 /**
00013  * xmlSaveOption:
00014  *
00015  * This is the set of XML save options that can be passed down
00016  * to the xmlSaveToFd() and similar calls.
00017  */
00018 typedef enum {
00019   XML_SAVE_FORMAT     = 1<<0  /* format save output */
00020 } xmlSaveOption;
00021 #endif
00022 
00023 using namespace std;
00024 
00025 unsigned int XMLLoadSave::AutoInit::libxmlrefc=0;
00026 
00027 XMLLoadSave::AutoInit::AutoInit() {
00028   if(libxmlrefc==0) {
00029     //cout << "libxmlinit" << endl;
00030     xmlInitParser();
00031     xmlSubstituteEntitiesDefault(1);
00032     xmlKeepBlanksDefault(1);
00033     xmlLineNumbersDefault(1);
00034     xmlIndentTreeOutput = 1;
00035   }
00036   libxmlrefc++;
00037 }
00038 
00039 XMLLoadSave::AutoInit::~AutoInit() {
00040   libxmlrefc--;
00041   if(libxmlrefc==0) {
00042     xmlCleanupParser();
00043     //cout << "libxmldest" << endl;
00044   }
00045 }
00046 
00047 
00048 #if LIBXML_VERSION >= 20623
00049 /**
00050  * xmlEscapeEntities: taken from libxml2/xmlsave.c, release 2.6.31
00051  * this version of the function is modified to avoid escaping entities which don't require it
00052  * (e.g. unicode characters)  This provides human-readable foreign language and symbols...
00053  *
00054  * @out:  a pointer to an array of bytes to store the result
00055  * @outlen:  the length of @out
00056  * @in:  a pointer to an array of unescaped UTF-8 bytes
00057  * @inlen:  the length of @in
00058  *
00059  * Take a block of UTF-8 chars in and escape them. Used when there is no
00060  * encoding specified.
00061  *
00062  * Returns 0 if success, or -1 otherwise
00063  * The value of @inlen after return is the number of octets consumed
00064  *     if the return value is positive, else unpredictable.
00065  * The value of @outlen after return is the number of octets consumed.
00066  */
00067 static int xmlEscapeMinimalEntities(unsigned char* out, int *outlen, const xmlChar* in, int *inlen) {
00068   unsigned char* outstart = out;
00069   const unsigned char* base = in;
00070   unsigned char* outend = out + *outlen;
00071   const unsigned char* inend;
00072   int val;
00073 
00074   inend = in + (*inlen);
00075   
00076   while ((in < inend) && (out < outend)) {
00077     if (*in == '<') {
00078       if (outend - out < 4) break;
00079       *out++ = '&';
00080       *out++ = 'l';
00081       *out++ = 't';
00082       *out++ = ';';
00083       in++;
00084       continue;
00085     } else if (*in == '>') {
00086       if (outend - out < 4) break;
00087       *out++ = '&';
00088       *out++ = 'g';
00089       *out++ = 't';
00090       *out++ = ';';
00091       in++;
00092       continue;
00093     } else if (*in == '&') {
00094       if (outend - out < 5) break;
00095       *out++ = '&';
00096       *out++ = 'a';
00097       *out++ = 'm';
00098       *out++ = 'p';
00099       *out++ = ';';
00100       in++;
00101       continue;
00102     } else if (((*in >= 0x20) && (*in < 0x80)) ||
00103            (*in == '\n') || (*in == '\t')) {
00104       /*
00105        * default case, just copy !
00106        */
00107       *out++ = *in++;
00108       continue;
00109     } else if (*in >= 0x80) {
00110       /*
00111        * We assume we have UTF-8 input.
00112        */
00113       if (outend - out < 10) break;
00114 
00115       if (*in < 0xC0) {
00116         std::cerr << "XMLLoadSave::xmlEscapeMinimalEntities encountered non-UTF8 data: " << *in << std::endl;
00117         in++;
00118         goto error;
00119       } else if (*in < 0xE0) {
00120         if (inend - in < 2) break;
00121         val = (in[0]) & 0x1F;
00122         val <<= 6;
00123         val |= (in[1]) & 0x3F;
00124         *out++ = *in++;
00125         *out++ = *in++;
00126       } else if (*in < 0xF0) {
00127         if (inend - in < 3) break;
00128         val = (in[0]) & 0x0F;
00129         val <<= 6;
00130         val |= (in[1]) & 0x3F;
00131         val <<= 6;
00132         val |= (in[2]) & 0x3F;
00133         *out++ = *in++;
00134         *out++ = *in++;
00135         *out++ = *in++;
00136       } else if (*in < 0xF8) {
00137         if (inend - in < 4) break;
00138         val = (in[0]) & 0x07;
00139         val <<= 6;
00140         val |= (in[1]) & 0x3F;
00141         val <<= 6;
00142         val |= (in[2]) & 0x3F;
00143         val <<= 6;
00144         val |= (in[3]) & 0x3F;
00145         *out++ = *in++;
00146         *out++ = *in++;
00147         *out++ = *in++;
00148         *out++ = *in++;
00149       } else {
00150         std::cerr << "XMLLoadSave::xmlEscapeMinimalEntities encountered invalid UTF8 data " << *in << std::endl;
00151         in++;
00152         goto error;
00153       }
00154       if (!IS_CHAR(val)) {
00155         std::cerr << "XMLLoadSave::xmlEscapeMinimalEntities encountered unknown UTF8 data " << *in << std::endl;
00156         goto error;
00157       }
00158 
00159     } else if (IS_BYTE_CHAR(*in)) {
00160       if (outend - out < 6) break;
00161       *out++ = *in++;
00162     } else {
00163       xmlGenericError(xmlGenericErrorContext,
00164         "xmlEscapeEntities : char out of range\n");
00165       in++;
00166       goto error;
00167     }
00168   }
00169   *outlen = out - outstart;
00170   *inlen = in - base;
00171   return(0);
00172 error:
00173   *outlen = out - outstart;
00174   *inlen = in - base;
00175   return(-1);
00176 }
00177 #endif
00178 
00179 XMLLoadSave::XMLLoadSave()
00180   : xmldocument(NULL), compressionLevel(-1), autoFormat(true), libxmlInit()
00181 {}
00182 
00183 XMLLoadSave::XMLLoadSave(const XMLLoadSave& xls)
00184   : LoadSave(xls), xmldocument(xls.xmldocument==NULL?NULL:xmlCopyDoc(xls.xmldocument,true)), compressionLevel(xls.compressionLevel), autoFormat(xls.autoFormat), libxmlInit()
00185 {}
00186 
00187 XMLLoadSave& XMLLoadSave::operator=(const XMLLoadSave& xls) {
00188   LoadSave::operator=(xls);
00189   if(xls.xmldocument==NULL)
00190     clearParseTree();
00191   else
00192     setParseTree(xmlCopyDoc(xls.xmldocument,true));
00193   compressionLevel = xls.compressionLevel;
00194   autoFormat=xls.autoFormat;
00195   return *this;
00196 }
00197 
00198 XMLLoadSave::~XMLLoadSave() {
00199   clearParseTree();
00200 }
00201 
00202 void XMLLoadSave::reportError(const std::string& context, const bad_format& err) const {
00203   cerr << context << endl;
00204   cerr << "  " << err.what() << endl;
00205   if(err.getNode()!=NULL) {
00206     xmlChar* path=xmlGetNodePath(err.getNode());
00207     xmlChar* uri = xmlNodeGetBase(err.getNode()->doc,err.getNode());
00208     std::string file;
00209     if(uri!=NULL && uri[0]!='\0')
00210       file = std::string(" of ") + (char*)uri + std::string(":");
00211     else
00212       file = " at line ";
00213     xmlFree(uri);
00214     cerr << "  Error was flagged during processing" << file << xmlGetLineNo(err.getNode()) << ":\n"
00215     << "    " << (char*)path << " '" << (char*)xmlNodeGetContent(err.getNode()) << '\'' << endl;
00216     xmlFree(path);
00217   }
00218 }
00219 
00220 unsigned int XMLLoadSave::getBinSize() const {
00221   try {
00222     if(xmldocument==NULL)
00223       setParseTree(xmlNewDoc((const xmlChar*)"1.0"));
00224     if(compressionLevel>=0)
00225       xmlSetDocCompressMode(xmldocument,compressionLevel);
00226     xmlNode * cur = FindRootXMLElement(xmldocument);
00227     saveXML(cur);
00228     xmlChar* buf=NULL;
00229     int size=0;
00230     // due to entity escaping unicode characters, this may predict a larger size than we actually use...
00231     // that's ok, this function is allowed to over-estimate size requirements
00232     xmlDocDumpFormatMemory(xmldocument, &buf, &size, autoFormat);
00233     xmlFree(buf);
00234     return size;
00235   } catch(const bad_format& err) {
00236     reportError("During calculation of size:",err);
00237     return 0;
00238   }
00239 }
00240 unsigned int XMLLoadSave::loadBuffer(const char buf[], unsigned int len, const char* filename) {
00241   if(xmldocument!=NULL) {
00242     xmlFreeDoc(xmldocument);
00243     xmldocument=NULL; // in case we error out below
00244   }
00245   
00246   //does actual low-level XML parsing
00247   xmlParserCtxt* ctxt=xmlCreateMemoryParserCtxt(buf,len);
00248   if(ctxt==NULL) {
00249     cerr << "Error: " << (filename ? filename : "") << " XMLLoadSave could not create memory parser context" << endl;
00250     return 0;
00251   }
00252   xmldocument = xmlParseDocument(ctxt)==0?ctxt->myDoc:NULL;
00253   if (xmldocument == NULL ) {
00254     cerr << "Error: XMLLoadSave buffer not parsed successfully. (xml syntax error)\n"
00255          << "       Attempting to recover..." << endl;
00256     xmlFreeParserCtxt(ctxt);
00257     ctxt=xmlCreateMemoryParserCtxt(buf,len);
00258     if(ctxt==NULL) {
00259       cerr << "Error: XMLLoadSave could not create memory parser context" << endl;
00260       return 0;
00261     }
00262     ctxt->recovery=1;
00263     xmldocument = xmlParseDocument(ctxt)==0?ctxt->myDoc:NULL;
00264     if(xmldocument==NULL) {
00265       cerr << "Error: XMLLoadSave recovery failed." << endl;
00266       xmlFreeParserCtxt(ctxt);
00267       return 0;
00268     }
00269   }
00270   unsigned int size=ctxt->nbChars;
00271   xmlFreeParserCtxt(ctxt);
00272   
00273   try {
00274     xmlNodePtr cur = FindRootXMLElement(xmldocument);
00275     loadXML(cur);
00276     return size;
00277   } catch(const bad_format& err) {
00278     reportError("During load of memory buffer:",err);
00279     xmlFreeDoc(xmldocument);
00280     xmldocument=NULL;
00281     return 0;
00282   }
00283 }
00284 unsigned int XMLLoadSave::saveBuffer(char buf[], unsigned int len) const {
00285   xmlBufferPtr xmlbuf = NULL;
00286   try {
00287     if(xmldocument==NULL)
00288       setParseTree(xmlNewDoc((const xmlChar*)"1.0"));
00289     if(compressionLevel>=0)
00290       xmlSetDocCompressMode(xmldocument,compressionLevel);
00291     xmlNode * cur = FindRootXMLElement(xmldocument);
00292     saveXML(cur);
00293 #if LIBXML_VERSION < 20623
00294     // versions prior to 2.6.23 don't have saveToBuffer implemented!
00295     (void)xmlbuf; // avoid warning, we don't use in this case...
00296     xmlChar* xbuf=NULL;
00297     int size=0;
00298     xmlDocDumpFormatMemory(xmldocument, &xbuf, &size, autoFormat);
00299     if((unsigned int)size<=len)
00300       memcpy(buf,xbuf,size);
00301     else {
00302       cerr << "Error: XMLLoadSave::saveBuffer xmlDocDumpFormatMemory returned larger region than the target buffer" << endl;
00303       size=0;
00304     }
00305     xmlFree(xbuf);
00306     return size;
00307 #else
00308     xmlbuf = xmlBufferCreate();
00309     xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(xmlbuf, NULL, (autoFormat ? XML_SAVE_FORMAT : 0));
00310     xmlSaveSetEscape(ctxt,xmlEscapeMinimalEntities);
00311     xmlSaveSetAttrEscape(ctxt,xmlEscapeMinimalEntities);
00312     size_t size = xmlSaveDoc(ctxt,xmldocument);
00313     xmlSaveClose(ctxt);
00314     ctxt=NULL;
00315     if(size==(size_t)-1) {
00316       cerr << "Error: XMLLoadSave::saveBuffer: xmlSaveDoc returned -1" << endl;
00317       return 0;
00318     }
00319     size=xmlBufferLength(xmlbuf);
00320     if(size<=len)
00321       memcpy(buf,xmlBufferContent(xmlbuf),size);
00322     else {
00323       cerr << "Error: XMLLoadSave::saveBuffer xmlSaveDoc returned larger region than the target buffer" << endl;
00324       size=0;
00325     }
00326     xmlBufferFree(xmlbuf);
00327     xmlbuf=NULL;
00328     return size;
00329 #endif
00330   } catch(const bad_format& err) {
00331     reportError("During save to memory buffer:",err);
00332     xmlBufferFree(xmlbuf);
00333     return 0;
00334   } catch(...) {
00335     xmlBufferFree(xmlbuf);
00336     throw;
00337   }
00338 }
00339 
00340 unsigned int XMLLoadSave::loadFile(const char* filename) {
00341   if(xmldocument!=NULL) {
00342     xmlFreeDoc(xmldocument);
00343     xmldocument=NULL; // in case we error out below
00344   }
00345   
00346   //does actual low-level XML parsing
00347   xmlParserCtxt* ctxt=xmlCreateFileParserCtxt(filename);
00348   if(ctxt==NULL) {
00349     cerr << "Error: XMLLoadSave could not create parser context for '"<< filename << "'" << endl;
00350     return 0;
00351   }
00352   xmldocument = xmlParseDocument(ctxt)==0?ctxt->myDoc:NULL;
00353   if (xmldocument == NULL ) {
00354     cerr << "Error: XMLLoadSave document '" << filename << "' not parsed successfully. (file not found or xml syntax error)\n"
00355     << "       Attempting to recover..." << endl;
00356     xmlFreeParserCtxt(ctxt);
00357     ctxt=xmlCreateFileParserCtxt(filename);
00358     if(ctxt==NULL) {
00359       cerr << "Error: XMLLoadSave could not create parser context for '"<< filename << "'" << endl;
00360       return 0;
00361     }
00362     ctxt->recovery=1;
00363     xmldocument = xmlParseDocument(ctxt)==0?ctxt->myDoc:NULL;
00364     if(xmldocument==NULL) {
00365       cerr << "Error: XMLLoadSave document '" << filename << "' recovery failed." << endl;
00366       xmlFreeParserCtxt(ctxt);
00367       return 0;
00368     }
00369   }
00370   unsigned int size=ctxt->nbChars;
00371   xmlFreeParserCtxt(ctxt);
00372   
00373   try {
00374     xmlNodePtr cur = FindRootXMLElement(xmldocument);
00375     loadXML(cur);
00376     return size;
00377   } catch(const bad_format& err) {
00378     string context("During load of '");
00379     context+=filename;
00380     context+="':";
00381     reportError(context,err);
00382     xmlFreeDoc(xmldocument);
00383     xmldocument=NULL;
00384     return 0;
00385   }
00386 }
00387 unsigned int XMLLoadSave::saveFile(const char* filename) const {
00388   xmlBufferPtr xmlbuf = NULL;
00389   try {
00390     if(xmldocument==NULL)
00391       setParseTree(xmlNewDoc((const xmlChar*)"1.0"));
00392     if(compressionLevel>=0)
00393       xmlSetDocCompressMode(xmldocument,compressionLevel);
00394     xmlNode * cur = FindRootXMLElement(xmldocument);
00395     saveXML(cur);
00396 #if LIBXML_VERSION < 20623
00397     // versions prior to 2.6.23 don't have saveToBuffer implemented!
00398     // could use xmlSaveToFilename and fake the return size, but I'd rather be correct and
00399     // give up on un-escaping fancy unicode characters
00400     (void)xmlbuf; // avoid warning, we don't use in this case...
00401     int size=xmlSaveFormatFile (filename, xmldocument, autoFormat);
00402     if(size==-1)
00403       cerr << "Error: XMLLoadSave::saveFile: xmlSaveFormatFile(\"" << filename << "\",...) returned -1" << endl;
00404     return size==-1?0:size;
00405 #else
00406     FILE* f = fopen(filename,"w");
00407     if(f==NULL) {
00408       std::cerr << "*** WARNING XMLLoadSave::saveFile: could not open file for saving \"" << filename << "\"" << std::endl;
00409       return 0;
00410     }
00411     // xmlSaveDoc doesn't properly return written size, so use buffers instead of xmlSaveToFilename:
00412     xmlbuf = xmlBufferCreate();
00413     xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(xmlbuf, NULL, (autoFormat ? XML_SAVE_FORMAT : 0));
00414     //xmlSaveCtxtPtr ctxt = xmlSaveToFilename(filename, NULL, (autoFormat ? XML_SAVE_FORMAT : 0));
00415     xmlSaveSetEscape(ctxt,xmlEscapeMinimalEntities);
00416     xmlSaveSetAttrEscape(ctxt,xmlEscapeMinimalEntities);
00417     size_t size = xmlSaveDoc(ctxt,xmldocument);
00418     xmlSaveClose(ctxt);
00419     ctxt=NULL;
00420     if(size==(size_t)-1) {
00421       cerr << "Error: XMLLoadSave::saveFile: xmlSaveDoc(\"" << filename << "\",...) returned -1" << endl;
00422       fclose(f);
00423       return 0;
00424     }
00425     size=xmlBufferLength(xmlbuf);
00426     size_t wrote=fwrite(xmlBufferContent(xmlbuf),1,xmlBufferLength(xmlbuf),f);
00427     if(wrote!=size)
00428       std::cerr << "*** WARNING XMLLoadSave::saveFile: short write (wrote " << wrote << ", expected " << size << ")" << std::endl;
00429     int err=fclose(f);
00430     if(err!=0) {
00431       std::cerr << "*** WARNING XMLLoadSave::saveFile: error '" << strerror(errno) << "' while closing " << filename << std::endl;
00432       return 0;
00433     }
00434     xmlBufferFree(xmlbuf);
00435     xmlbuf=NULL;
00436     return size;
00437 #endif
00438   } catch(const bad_format& err) {
00439     string context("During save to '");
00440     context+=filename;
00441     context+="':";
00442     reportError(context,err);
00443     xmlBufferFree(xmlbuf);
00444     return 0;
00445   } catch(...) {
00446     xmlBufferFree(xmlbuf);
00447     throw;
00448   }
00449 }
00450 
00451 unsigned int XMLLoadSave::loadFileStream(FILE* f, const char* filename) {
00452   if(xmldocument!=NULL) {
00453     xmlFreeDoc(xmldocument);
00454     xmldocument=NULL; // in case we error out below
00455   }
00456   
00457   //does actual low-level XML parsing
00458   //This is a little sketchy trying to shoehorn a SAX style call, but it seems to work
00459   xmlParserCtxt* ctxt=xmlCreateIOParserCtxt(NULL,NULL,fileReadCallback,fileCloseCallback,f,XML_CHAR_ENCODING_UTF8);
00460   if(ctxt==NULL) {
00461     cerr << "Error: XMLLoadSave could not create file stream parser context" << endl;
00462     return 0;
00463   }
00464   ctxt->recovery=1;
00465   xmldocument = (xmlParseDocument(ctxt)==0) ? ctxt->myDoc : NULL;
00466   unsigned int size=ctxt->nbChars;
00467   bool wellFormed=ctxt->wellFormed;
00468   xmlFreeParserCtxt(ctxt);
00469   if (xmldocument==NULL) {
00470     cerr << "Error: XMLLoadSave file stream not parsed successfully. (xml syntax error)\n" << endl;
00471     return 0;
00472   }
00473   if(!wellFormed)
00474     cerr << "Warning: XMLLoadSave file stream was not well formed (but was recovered)." << endl;
00475   
00476   try {
00477     xmlNodePtr cur = FindRootXMLElement(xmldocument);
00478     loadXML(cur);
00479     return size;
00480   } catch(const bad_format& err) {
00481     reportError("During load of file stream "+std::string(filename)+":",err);
00482     xmlFreeDoc(xmldocument);
00483     xmldocument=NULL;
00484     return 0;
00485   }
00486 }
00487 unsigned int XMLLoadSave::saveFileStream(FILE* f) const {
00488   xmlBufferPtr xmlbuf = NULL;
00489   try {
00490     if(xmldocument==NULL)
00491       setParseTree(xmlNewDoc((const xmlChar*)"1.0"));
00492     if(compressionLevel>=0)
00493       xmlSetDocCompressMode(xmldocument,compressionLevel);
00494     xmlNode * cur = FindRootXMLElement(xmldocument);
00495     saveXML(cur);
00496 #if LIBXML_VERSION < 20623
00497     // versions prior to 2.6.23 don't have saveToBuffer implemented!
00498     // could use xmlSaveToFilename and fake the return size, but I'd rather be correct and
00499     // give up on un-escaping fancy unicode characters
00500     (void)xmlbuf; // avoid warning, we don't use in this case...
00501     int size=xmlDocFormatDump(f, xmldocument, autoFormat);
00502     if(size==-1)
00503       cerr << "Error: XMLLoadSave::saveFileStream: xmlDocFormatDump(...) returned -1" << endl;
00504     return size==-1?0:size;
00505 #else
00506     // xmlSaveDoc doesn't properly return written size, so use buffers instead of xmlSaveToFd:
00507     xmlbuf = xmlBufferCreate();
00508     xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(xmlbuf, NULL, (autoFormat ? XML_SAVE_FORMAT : 0));
00509     //xmlSaveCtxtPtr ctxt = xmlSaveToFd([...], NULL, (autoFormat ? XML_SAVE_FORMAT : 0));
00510     xmlSaveSetEscape(ctxt,xmlEscapeMinimalEntities);
00511     xmlSaveSetAttrEscape(ctxt,xmlEscapeMinimalEntities);
00512     size_t size = xmlSaveDoc(ctxt,xmldocument);
00513     xmlSaveClose(ctxt);
00514     ctxt=NULL;
00515     if(size==(size_t)-1) {
00516       cerr << "Error: XMLLoadSave::saveFileStream: xmlSaveDoc returned -1" << endl;
00517       return 0;
00518     }
00519     size=xmlBufferLength(xmlbuf);
00520     size_t wrote=fwrite(xmlBufferContent(xmlbuf),1,xmlBufferLength(xmlbuf),f);
00521     if(wrote!=size)
00522       std::cerr << "*** WARNING XMLLoadSave::saveFileStream: short write (wrote " << wrote << ", expected " << size << ")" << std::endl;
00523     xmlBufferFree(xmlbuf);
00524     xmlbuf=NULL;
00525     return size;
00526 #endif
00527   } catch(const bad_format& err) {
00528     reportError("During save to file stream:",err);
00529     xmlBufferFree(xmlbuf);
00530     return 0;
00531   } catch(...) {
00532     xmlBufferFree(xmlbuf);
00533     throw;
00534   }
00535 }
00536 
00537 //! Extends the parser context to include a completion flag and the input stream, and encapsulate the setup
00538 struct StreamParser {
00539   // This must go first to masquerade as a xmlParserCtxt
00540   // Tried to use inheritance, but get a warning about synthesized constructor, rather just encapsulate instead
00541   // I know it's ugly(ier), don't hate me, it removes the warning so now no one will ever be compelled to look here anyway.
00542   xmlParserCtxt _ctxt;
00543   
00544   //! constructor
00545   StreamParser(std::istream& in, bool asFragment) : _ctxt(), handler(), ctxt(NULL), complete(false), is(&in), used(0) {
00546     if(!asFragment) {
00547       ctxt = xmlCreatePushParserCtxt(NULL,NULL,NULL,0,NULL);
00548     } else {
00549       xmlSAXVersion(&handler,1); // using SAX version 1... bad?  endElement is much simpler than endElementNs
00550       handler.endElement = endElementCallback;
00551       ctxt = xmlCreatePushParserCtxt(&handler,NULL,NULL,0,NULL);
00552     }
00553     if(ctxt==NULL) {
00554       cerr << "Error: XMLLoadSave could not create file stream parser context" << endl;
00555       throw std::bad_alloc();
00556     }
00557     ctxt->recovery=1;
00558     ctxt->userData=this;
00559     _ctxt=(*ctxt); // now steal all the associated structures
00560   }
00561   //! destructor, delete the context
00562   ~StreamParser() { xmlFreeParserCtxt(ctxt); ctxt=NULL; }
00563   
00564   operator xmlParserCtxt*() { return &_ctxt; }
00565   
00566   //! stores callbacks, uses defaults to build tree, except endElement which forwards to our local version
00567   xmlSAXHandler handler;
00568   //! the original libxml structure we're mimicing, stored so we can free it in destructor (and thus the associated data structures)
00569   xmlParserCtxt* ctxt;
00570   
00571   //! marked true once the root element is closed
00572   bool complete;
00573   //! the stream we're reading from (so we can putback extra data for the next read)
00574   std::istream* is;
00575   //! should be updated by pusher to note how much data was loaded by the stream
00576   /*! (probably can't putback more than this, but also allows us to track usage byte count accurately) */
00577   unsigned int used;
00578   
00579   //! forwards callback to doEndElement() so we don't have to prepend @a ctx on every usage
00580   static void endElementCallback(void * ctx, const xmlChar * name) { reinterpret_cast<StreamParser*>(ctx)->doEndElement(name); }
00581   
00582   //! check if this is ending the root element, and putback unused data if it is, then mark the input complete
00583   void doEndElement(const xmlChar * curName) {
00584     //std::cerr << "Root is " << xmlDocGetRootElement(myDoc) << ' ' << node << ' ' << nodeNr << ' ' << nameNr << ' ' << spaceNr << std::endl;
00585     if(_ctxt.nameNr==1) {
00586       for(const xmlChar* x=_ctxt.input->end; x!=_ctxt.input->cur && *is; )
00587         is->putback(*--x);
00588       //std::cout << "put back " << input->end - input->cur << std::endl;
00589       used-=(_ctxt.input->end - _ctxt.input->cur);
00590       _ctxt.input->cur=_ctxt.input->end; // this prevents libxml from seeing the extra data, also seems to signal libxml not to try to get more data if letting the library pull
00591       complete=true;
00592     }
00593     xmlSAX2EndElement(this,curName);
00594   }
00595 private:
00596   StreamParser(const StreamParser&); //!< don't call
00597   StreamParser& operator=(const StreamParser&); //!< don't call
00598 };
00599 
00600 unsigned int XMLLoadSave::loadStream(std::istream& is, bool asFragment /*=false*/) {
00601   if(xmldocument!=NULL) {
00602     xmlFreeDoc(xmldocument);
00603     xmldocument=NULL; // in case we error out below
00604   }
00605   
00606   unsigned int totalUsed=0;
00607   {
00608     StreamParser parser(is,asFragment);
00609     const size_t BUFSIZE=4*1024;
00610     char buf[BUFSIZE];
00611     while(!parser.complete && is.read(buf,1)) { // block for more data
00612       is.readsome(buf+1,BUFSIZE-1); // read rest of the packet (doesn't necessarily fill buffer, just what's available...)
00613       parser.used = is.gcount()+1;
00614       //std::cout << this << " buffer is '" << std::string(buf,std::min(100,used)) << "'" << std::endl;
00615       int err = xmlParseChunk(parser,buf,parser.used,false);
00616       if(err!=0) {
00617         std::cerr << "XMLLoadSave::loadStream encountered XML error " << err << std::endl;
00618         return 0;
00619       }
00620       totalUsed+=parser.used;
00621     }
00622     if(!is || !parser.complete)
00623       return 0;
00624     int err = xmlParseChunk(parser,NULL,0,true);
00625     if(err!=0) {
00626       std::cerr << "XMLLoadSave::loadStream encountered XML error " << err << std::endl;
00627       return 0;
00628     }
00629     if(!parser._ctxt.wellFormed)
00630       cerr << "Warning: XMLLoadSave file stream was not well formed (but was recovered)." << endl;
00631     
00632     xmldocument = parser._ctxt.myDoc;
00633     if(xmldocument==NULL) {
00634       cerr << "ERROR: XMLLoadSave parsing completed but document is still NULL!" << endl;
00635       return 0;
00636     }
00637   }
00638   
00639   try {
00640     xmlNodePtr cur = FindRootXMLElement(xmldocument);
00641     loadXML(cur);
00642     return totalUsed;
00643   } catch(const bad_format& err) {
00644     reportError("During load of file stream:",err);
00645     xmlFreeDoc(xmldocument);
00646     xmldocument=NULL;
00647     return 0;
00648   }
00649 }
00650 unsigned int XMLLoadSave::saveStream(std::ostream& os, bool asFragment) const {
00651   xmlBufferPtr xmlbuf = NULL;
00652   try {
00653     if(xmldocument==NULL)
00654       setParseTree(xmlNewDoc((const xmlChar*)"1.0"));
00655     if(compressionLevel>=0)
00656       xmlSetDocCompressMode(xmldocument,compressionLevel);
00657     xmlNode * cur = FindRootXMLElement(xmldocument);
00658     saveXML(cur);
00659 #if LIBXML_VERSION < 20623
00660     // versions prior to 2.6.23 don't have saveToBuffer implemented!
00661     (void)xmlbuf; // avoid warning, we don't use in this case...
00662     xmlChar* xbuf=NULL;
00663     int size=0;
00664     xmlDocDumpFormatMemory(xmldocument, &xbuf, &size, autoFormat);
00665     os.write((const char*)xbuf,size);
00666     xmlFree(xbuf);
00667     return size;
00668 #else
00669     xmlbuf = xmlBufferCreate();
00670     xmlSaveCtxtPtr ctxt = xmlSaveToBuffer(xmlbuf, NULL, (autoFormat ? XML_SAVE_FORMAT : 0));
00671     xmlSaveSetEscape(ctxt,xmlEscapeMinimalEntities);
00672     xmlSaveSetAttrEscape(ctxt,xmlEscapeMinimalEntities);
00673     size_t size = asFragment ? xmlSaveTree(ctxt,xmlDocGetRootElement(xmldocument)) : xmlSaveDoc(ctxt,xmldocument);
00674     xmlSaveClose(ctxt);
00675     ctxt=NULL;
00676     if(size==(size_t)-1) {
00677       cerr << "Error: XMLLoadSave::saveBuffer: xmlSaveDoc/xmlSaveTree returned -1" << endl;
00678       return 0;
00679     }
00680     size=xmlBufferLength(xmlbuf);
00681     os.write((const char*)xmlBufferContent(xmlbuf),size);
00682     xmlBufferFree(xmlbuf);
00683     return size;
00684 #endif
00685   } catch(const bad_format& err) {
00686     reportError("During save to memory buffer:",err);
00687     xmlBufferFree(xmlbuf);
00688     return 0;
00689   } catch(...) {
00690     xmlBufferFree(xmlbuf);
00691     throw;
00692   }
00693 }
00694 
00695 void XMLLoadSave::clearParseTree() {
00696   xmlFreeDoc(xmldocument);
00697   xmldocument=NULL;
00698 }
00699 void XMLLoadSave::setParseTree(xmlDoc* doc) const {
00700   if(doc==xmldocument)
00701     return;
00702   xmlFreeDoc(xmldocument);
00703   xmldocument=doc;
00704 }
00705 xmlDoc* XMLLoadSave::stealParseTree(xmlDoc* newdoc/*=NULL*/) const {
00706   xmlDoc * oldDoc = xmldocument;
00707   xmldocument=newdoc;
00708   return oldDoc;
00709 }
00710 void XMLLoadSave::readParseTree() {
00711   if(xmldocument==NULL)
00712     return;
00713   try {
00714     xmlNodePtr cur = FindRootXMLElement(xmldocument);
00715     loadXML(cur);
00716   } catch(const bad_format& err) {
00717     reportError("During XMLLoadSave::readParseTree:",err);
00718     xmlFreeDoc(xmldocument);
00719     xmldocument=NULL;
00720   }
00721 }
00722 void XMLLoadSave::writeParseTree() const {
00723   try {
00724     if(xmldocument==NULL)
00725       setParseTree(xmlNewDoc((const xmlChar*)"1.0"));
00726     if(compressionLevel>=0)
00727       xmlSetDocCompressMode(xmldocument,compressionLevel);
00728     xmlNode * cur = FindRootXMLElement(xmldocument);
00729     saveXML(cur);
00730   } catch(const bad_format& err) {
00731     reportError("During writeParseTree:",err);
00732   }
00733 }
00734 
00735 void XMLLoadSave::setCompression(int level) {
00736   compressionLevel=level;
00737   if(xmldocument!=NULL)
00738     xmlSetDocCompressMode(xmldocument,compressionLevel);
00739 }
00740 
00741 xmlNode* XMLLoadSave::FindRootXMLElement(xmlDoc* doc) const {
00742   if(doc==NULL)
00743     return NULL;
00744   xmlNode* cur=xmlDocGetRootElement(doc);
00745   if(cur==NULL) {
00746     //empty file
00747     cur = xmlNewNode(NULL,(const xmlChar*)"");
00748     xmlFree(xmlDocSetRootElement(doc,cur));
00749   } 
00750   return cur;
00751 }
00752 
00753 int XMLLoadSave::fileReadCallback(void* f,char* buf, int len) {
00754   return ferror((FILE*)f) ? -1 : (int)fread(buf,sizeof(char),len,(FILE*)f);
00755 }
00756 int XMLLoadSave::fileCloseCallback(void* f) {
00757   return fclose((FILE*)f) ? -1 : 0;
00758 }
00759 
00760 xmlNode* XMLLoadSave::skipToElement(xmlNode* cur) {
00761   while(cur!=NULL && cur->type!=XML_ELEMENT_NODE)
00762     cur=cur->next;
00763   return cur;
00764 }
00765 
00766 xmlNode* XMLLoadSave::skipToElement(xmlNode* cur, std::string& comment) {
00767   comment.clear();
00768   while(cur!=NULL && cur->type!=XML_ELEMENT_NODE) {
00769     if(cur->type==XML_COMMENT_NODE) {
00770       xmlChar* cont=xmlNodeGetContent(cur);
00771       comment=(char*)cont; //only take last comment in series
00772       xmlFree(cont);
00773     }
00774     cur=cur->next;
00775   }
00776   return cur;
00777 }
00778 
00779 /*! @file
00780  * @brief 
00781  * @author Ethan Tira-Thompson (ejt) (Creator)
00782  */

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:53 2016 by Doxygen 1.6.3