From da0215dacf10d534947e5dcbfc41236ed9dceb3b Mon Sep 17 00:00:00 2001 From: David Koes Date: Wed, 10 Jun 2015 17:02:47 -0400 Subject: [PATCH 01/20] checkpoint work on obconversion fix still broken --- CMakeLists.txt | 12 +- include/openbabel/format.h | 5 +- include/openbabel/lineend.h | 18 ++ include/openbabel/obconversion.h | 66 +++-- src/CMakeLists.txt | 6 +- src/formats/xml/xml.cpp | 1 - src/obconversion.cpp | 478 +++++++++++++++---------------- src/obmolecformat.cpp | 4 +- 8 files changed, 304 insertions(+), 286 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7cf903036..0212c08571 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,16 +166,12 @@ if(NOT LIBXML2_FOUND) message(WARNING "libxml2 not found - disabling CML support!") endif() -find_package(ZLIB) -if(ZLIB_FOUND) - add_definitions(-DHAVE_LIBZ) - include_directories(${ZLIB_INCLUDE_DIR}) - # Longstanding unsolved problem with compression under Windows - if(WIN32) - add_definitions(-DDISABLE_WRITE_COMPRESSION) - endif() +find_package(Boost 1.36.0 COMPONENTS iostreams) +if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) endif() + # wxWidgets instructions based on http://wiki.wxwidgets.org/CMake #find_package(wxWidgets COMPONENTS base core REQUIRED) find_package(wxWidgets COMPONENTS base core adv) diff --git a/include/openbabel/format.h b/include/openbabel/format.h index e82c27b3da..a803e26180 100644 --- a/include/openbabel/format.h +++ b/include/openbabel/format.h @@ -148,6 +148,9 @@ std::cerr << "Not a valid input format"; return false;} static OBFormat* FormatFromMIME(const char* MIME); + enum CompressionType {NONE,GZIP}; //someday we will support bzip2 + virtual CompressionType GetCompression() const { return compression; } + virtual void SetCompression(CompressionType val) { compression = val; } private: static PluginMapType &FormatsMIMEMap() { @@ -156,7 +159,7 @@ std::cerr << "Not a valid input format"; return false;} } const char* pMime; - + CompressionType compression; /* Functions provided by the MAKE_PLUGIN macro ///Constructor that registers the ID of the format diff --git a/include/openbabel/lineend.h b/include/openbabel/lineend.h index d1b500e78e..f701e1261e 100644 --- a/include/openbabel/lineend.h +++ b/include/openbabel/lineend.h @@ -186,6 +186,24 @@ class OBCONV LineEndingExtractor void finalize( std::streambuf& ) {} }; +/*! \class FilteringInputStream lineend.h + \brief A stream interface for FilteringInputStreambuf + */ +template +class FilteringInputStream : + public FilteringInputStreambuf, + public std::istream +{ +public: + typedef std::istream& istream_reference; + typedef std::istream istream_type; + + explicit FilteringInputStream(istream_reference istream): + FilteringInputStreambuf(istream.rdbuf()),std::istream(this) {} + virtual ~FilteringInputStream() {} + +}; + } //namespace #endif //OB_LINEEND_H diff --git a/include/openbabel/obconversion.h b/include/openbabel/obconversion.h index 2721563c6a..014d5aa3ef 100644 --- a/include/openbabel/obconversion.h +++ b/include/openbabel/obconversion.h @@ -24,6 +24,7 @@ GNU General Public License for more details. #include #include #include +#include #include #include @@ -95,25 +96,16 @@ namespace OpenBabel { static const char* Description(); //generic conversion options //@} + /// These return a filtered stream for reading/writing (possible filters include compression, decompression, and newline transformation) /// @name Parameter get and set //@{ - std::istream* GetInStream() const {return pInStream;}; - std::ostream* GetOutStream() const {return pOutStream;}; - void SetInStream(std::istream* pIn) - { - if (pInStream && NeedToFreeInStream) { - delete pInStream; NeedToFreeInStream = false; - } - pInStream=pIn; - CheckedForGzip = false; // haven't tried to gzip decode this stream - }; - void SetOutStream(std::ostream* pOut) - { - if (pOutStream && NeedToFreeOutStream) { - delete pOutStream; NeedToFreeOutStream = false; - } - pOutStream=pOut; - }; + std::istream* GetInStream() const {return pInput;}; + std::ostream* GetOutStream() const {return pOutput;}; + + /// @brief Set input stream. If takeOwnership is true, will deallocate when done. + void SetInStream(std::istream* pIn, bool takeOwnership=false); + void SetOutStream(std::ostream* pOut, bool takeOwnership=false); + /// Sets the formats from their ids, e g CML bool SetInAndOutFormats(const char* inID, const char* outID); bool SetInAndOutFormats(OBFormat* pIn, OBFormat* pOut); @@ -298,6 +290,7 @@ namespace OpenBabel { /// Part of "API" interface. /// \return false and pOb=NULL on error /// This method is primarily intended for scripting languages without "stream" classes + /// Any existing input stream will be replaced by stringstream. bool ReadString(OBBase* pOb, std::string input); /// @brief Reads an object of a class derived from OBBase into pOb from the file specified @@ -336,22 +329,47 @@ namespace OpenBabel { static std::string IncrementedFileName(std::string& BaseName, const int Count); ///Checks for misunderstandings when using the -m option static bool CheckForUnintendedBatch(const std::string& infile, const std::string& outfile); - ///Adds a filtering rdbuffer to handle line endings if not already installed and not a binary or xml format. - void InstallStreamFilter(); + void ClearInStreams(); //@} protected: + + //helper class for saving stream state + struct StreamState + { + std::ios *pStream; //active stream + std::vector ownedStreams; //streams we own the memory to + + StreamState(): pStream(NULL) {} + ~StreamState() + { + assert(ownedStreams.size() == 0); //should be popped + } + + void pushOutput(OBConversion& conv); + void popOutput(OBConversion& conv); + }; + bool SetStartAndEnd(); // static FMapType& FormatsMap();/// OPAMapType; static OPAMapType& OptionParamArray(Option_type typ); bool OpenAndSetFormat(bool SetFormat, std::ifstream* is, std::stringstream* ss=NULL); + bool LooksLikeGZip(std::istream *pIn) const; std::string InFilename, OutFilename; //OutFileName added v2.4.0 - std::istream* pInStream; - std::ostream* pOutStream; + + typedef FilteringInputStream< LineEndingExtractor > LEInStream; + + std::istream *pInput; //input stream, may be filtered + std::vector ownedInStreams; //streams we own the memory to + + std::ostream *pOutput; //output stream, may have filters applied + std::vector ownedOutStreams; //streams we own the memory to + + static OBFormat* pDefaultFormat; OBFormat* pInFormat; OBFormat* pOutFormat; @@ -367,12 +385,8 @@ namespace OpenBabel { bool MoreFilesToCome; bool OneObjectOnly; bool ReadyToInput; - bool CheckedForGzip; ///< input stream is gzip-encoded bool SkippedMolecules; /// skip molecules using -f and -l - bool NeedToFreeInStream; - bool NeedToFreeOutStream; - typedef FilteringInputStreambuf< LineEndingExtractor > LErdbuf; - LErdbuf* pLineEndBuf; + OBBase* pOb1; std::streampos wInpos; ///SetAuxConv(this);//marks original OBConversion object as having been extended SetAuxConv(this);//marks this new object as extended (for use with OBConversion pointer) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index 2a45a682fe..428c1dd921 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -56,6 +56,12 @@ GNU General Public License for more details. extern "C" int strncasecmp(const char *s1, const char *s2, size_t n); #endif +#include +#include +#include +#include +#include + #ifndef BUFF_SIZE #define BUFF_SIZE 32768 #endif @@ -227,14 +233,14 @@ namespace OpenBabel { // OBFormat* OBConversion::pDefaultFormat=NULL; OBConversion::OBConversion(istream* is, ostream* os) : + pInput(NULL), pOutput(NULL), pInFormat(NULL),pOutFormat(NULL), Index(0), StartNumber(1), EndNumber(0), Count(-1), m_IsFirstInput(true), m_IsLast(true), - MoreFilesToCome(false), OneObjectOnly(false), CheckedForGzip(false), - NeedToFreeInStream(false), NeedToFreeOutStream(false), SkippedMolecules(false), - pOb1(NULL), pAuxConv(NULL),pLineEndBuf(NULL),wInpos(0),wInlen(0) + MoreFilesToCome(false), OneObjectOnly(false), SkippedMolecules(false), + pOb1(NULL), pAuxConv(NULL),wInpos(0),wInlen(0) { - pInStream=is; - pOutStream=os; + SetInStream(is); + SetOutStream(os); //These options take a parameter RegisterOptionParam("f", NULL, 1,GENOPTIONS); @@ -244,14 +250,18 @@ namespace OpenBabel { ///////////////////////////////////////////////// OBConversion::OBConversion(const OBConversion& o) { + //the original obconversion retains ownership of any allocated streams + //this means if the original gets destroyed, bad things may happen + //by doing this first, format isn't initialized, so we add no additional filters + SetInStream(o.pInput, false); + SetOutStream(o.pOutput, false); + Index = o.Index; Count = o.Count; StartNumber = o.StartNumber; EndNumber = o.EndNumber; pInFormat = o.pInFormat; - pInStream = o.pInStream; pOutFormat = o.pOutFormat; - pOutStream = o.pOutStream; OptionsArray[0]= o.OptionsArray[0]; OptionsArray[1]= o.OptionsArray[1]; OptionsArray[2]= o.OptionsArray[2]; @@ -266,11 +276,7 @@ namespace OpenBabel { pOb1 = o.pOb1; ReadyToInput = o.ReadyToInput; m_IsFirstInput = o.m_IsFirstInput; - CheckedForGzip = o.CheckedForGzip; SkippedMolecules = o.SkippedMolecules; - NeedToFreeInStream = o.NeedToFreeInStream; - NeedToFreeOutStream = o.NeedToFreeOutStream; - pLineEndBuf = o.pLineEndBuf; pAuxConv = NULL; } /////////////////////////////////////////////// @@ -281,23 +287,11 @@ namespace OpenBabel { if(pAuxConv) { delete pAuxConv; - //pAuxConv has copies of pInStream, NeedToFreeInStream, pOutStream, NeedToFreeOutStream - //and may have already deleted the streams. So do not do it again. - NeedToFreeInStream = NeedToFreeOutStream = false; } // Free any remaining streams from convenience functions - if(pInStream && NeedToFreeInStream) { - delete pInStream; - pInStream=NULL; - NeedToFreeInStream = false; - } - if(pOutStream && NeedToFreeOutStream) { - delete pOutStream; - pOutStream=NULL; - NeedToFreeOutStream = false; - } -// delete pLineEndBuf; -// pLineEndBuf=NULL; + SetInStream(NULL); + SetOutStream(NULL); + } ////////////////////////////////////////////////////// @@ -312,7 +306,77 @@ namespace OpenBabel { return pFormat->RegisterFormat(ID, MIME); } - ////////////////////////////////////////////////////// + /// Set input stream, removing/deallocating previous stream if necessary. + /// If takeOwnership is true, takes responsibility for freeing pIn + void OBConversion::SetInStream(std::istream* pIn, bool takeOwnership) + { + //clear and deallocate any existing streams + for(unsigned i = 0, n = ownedInStreams.size(); i < n; i++) + { + delete ownedInStreams[i]; + } + ownedInStreams.clear(); + pInput = NULL; + + if (pIn) + { + if(takeOwnership) + ownedInStreams.push_back(pIn); + pInput = pIn; //simplest case + + #ifdef HAVE_LIBZ + if(pInFormat && pInFormat->GetCompression() != OBFormat::NONE) + { + zlib_stream::zip_istream *zIn = new zlib_stream::zip_istream(*pInput); + ownedInStreams.push_back(zIn); + pInput = zIn; + } + #endif + //always transform newlines if input isn't binary/xml + if(pInFormat && !(pInFormat->Flags() & (READBINARY | READXML)) && + pIn != &std::cin) //avoid filtering stdin as well + { + LEInStream *leIn = new LEInStream(*pInput); + ownedInStreams.push_back(leIn); + pInput = leIn; + } + } + } + + /// Set output stream, removing/deallocating previous stream if necessary. + /// If takeOwnership is true, takes responsibility for freeing pOut + void OBConversion::SetOutStream(std::ostream* pOut, bool takeOwnership) + { + //clear and deallocate any existing streams + for (unsigned i = 0, n = ownedOutStreams.size(); i < n; i++) + { + delete ownedOutStreams[i]; + } + ownedOutStreams.clear(); + pOutput = NULL; + + if (pOut) + { + if (takeOwnership) + ownedOutStreams.push_back(pOut); + pOutput = pOut; + +#ifdef HAVE_LIBZ + + if ( (pOutFormat && pOutFormat->GetCompression() != OBFormat::NONE) || + IsOption("z", GENOPTIONS)) + { + zlib_stream::zip_ostream *zOut = new zlib_stream::zip_ostream(*pOutput); + ownedOutStreams.push_back(zOut); + pOutput = zOut; + } +#endif + } + } + + + +////////////////////////////////////////////////////// /// Sets the formats from their ids, e g CML. /// If inID is NULL, the input format is left unchanged. Similarly for outID /// Returns true if both formats have been successfully set at sometime @@ -357,58 +421,14 @@ namespace OpenBabel { } ////////////////////////////////////////////////////// + /// Convert molecules from is into os. If either is null, uses existing streams. + /// Unlike other methods that take a stream, if a stream is specified, it does _not_ replace the existing stream. int OBConversion::Convert(istream* is, ostream* os) { - if (is) { - pInStream=is; - CheckedForGzip = false; // haven't checked this for gzip yet - } - if (os) pOutStream=os; - ostream* pOrigOutStream = pOutStream; - -#ifdef HAVE_LIBZ - zlib_stream::zip_istream *zIn; - - // only try to decode the gzip stream once - if (!CheckedForGzip) { - zIn = new zlib_stream::zip_istream(*pInStream); - if (zIn->is_gzip()) { - pInStream = zIn; - CheckedForGzip = true; - } - else { - delete zIn; - zIn = NULL; - } - } -#ifndef DISABLE_WRITE_COMPRESSION //Unsolved problem with compression under Windows - zlib_stream::zip_ostream zOut(*pOutStream); - if(IsOption("z",GENOPTIONS)) - { - // make sure to output the header - zOut.make_gzip(); - pOutStream = &zOut; - } -#endif -#endif - - //The FilteringInputStreambuf delivers characters to the istream, pInStream, - //and receives characters this stream's original rdbuf. - //It filters them, converting CRLF and CR line endings to LF. - //seek and tellg requests to the stream are passed through to the original - //rdbuf. A FilteringInputStreambuf is installed only for appropriate formats - //- not for binary or XML formats - if not already present. - InstallStreamFilter(); + if (is) SetInStream(is, false); + if (os) SetOutStream(os, false); int count = Convert(); - - pOutStream = pOrigOutStream; -#ifdef HAVE_LIBZ - if ( CheckedForGzip && zIn != NULL){ // Bug reported by Gert Thijs - delete zIn; - pInStream = is; - } -#endif return count; } @@ -433,7 +453,7 @@ namespace OpenBabel { /// int OBConversion::Convert() { - if(pInStream==NULL || pOutStream==NULL) + if(pInput==NULL) { obErrorLog.ThrowError(__FUNCTION__, "input or output stream not set", obError); return 0; @@ -454,16 +474,15 @@ namespace OpenBabel { OneObjectOnly=true; //Input loop - while(ReadyToInput && pInStream->good()) //Possible to omit? && pInStream->peek() != EOF + while(ReadyToInput && pInput->good()) //Possible to omit? && pInStream->peek() != EOF { - if(pInStream==&cin) + if(pInput==&cin) { - if(pInStream->peek()==-1) //Cntl Z Was \n but interfered with piping + if(pInput->peek()==-1) //Cntl Z Was \n but interfered with piping break; } else - rInpos = pInStream->tellg(); - + rInpos = pInput->tellg(); bool ret=false; #ifndef DONT_CATCH_EXCEPTIONS try @@ -610,7 +629,7 @@ namespace OpenBabel { if(Count==(int)EndNumber) ReadyToInput=false; //stops any more objects being read - rInlen = pInStream ? pInStream->tellg() - rInpos : 0; + rInlen = pInput ? pInput->tellg() - rInpos : 0; // - (pLineEndBuf ? pLineEndBuf->getCorrection() : 0); //correction for CRLF if(pOb) @@ -709,16 +728,18 @@ namespace OpenBabel { && (file.substr(extPos + 1, file.size())).find("/")==string::npos) // and period is after the last "/" { // only do this if we actually can read .gz files -#ifdef HAVE_LIBZ if (file.substr(extPos) == ".gz") { file.erase(extPos); extPos = file.rfind('.'); if (extPos!=string::npos) - return FindFormat( (file.substr(extPos + 1, file.size())).c_str() ); + { + OBFormat *ret = FindFormat( (file.substr(extPos + 1, file.size())).c_str() ); + if(ret) ret->SetCompression(OBFormat::GZIP); + return ret; + } } else -#endif return FindFormat( (file.substr(extPos + 1, file.size())).c_str() ); } @@ -744,36 +765,23 @@ namespace OpenBabel { bool OBConversion::Read(OBBase* pOb, std::istream* pin) { if(pin) { - pInStream=pin; - CheckedForGzip = false; // haven't set this stream to gzip (yet) - } - - if(!pInFormat || !pInStream) return false; - -#ifdef HAVE_LIBZ - zlib_stream::zip_istream *zIn; - - // only try to decode the gzip stream once - if (!CheckedForGzip) { - zIn = new zlib_stream::zip_istream(*pInStream); - if (zIn->is_gzip()) { - pInStream = zIn; - CheckedForGzip = true; + //for backwards compatibility, attempt to detect a gzip file + if(pInFormat && LooksLikeGZip(pin)) + { + pInFormat->SetCompression(OBFormat::GZIP); } - else - delete zIn; + SetInStream(pin, false); } -#endif - InstallStreamFilter(); + if(!pInFormat || !pInput) return false; // Set the locale for number parsing to avoid locale issues: PR#1785463 obLocale.SetLocale(); // Also set the C++ stream locale - locale originalLocale = pInStream->getloc(); // save the original + locale originalLocale = pInput->getloc(); // save the original locale cNumericLocale(originalLocale, "C", locale::numeric); - pInStream->imbue(cNumericLocale); + pInput->imbue(cNumericLocale); // skip molecules if -f or -l option is set if (!SkippedMolecules) { @@ -794,11 +802,11 @@ namespace OpenBabel { // return the C locale to the original one obLocale.RestoreLocale(); // Restore the original C++ locale as well - pInStream->imbue(originalLocale); + pInput->imbue(originalLocale); // If we failed to read, plus the stream is over, then check if this is a stream from ReadFile - if (!success && !pInStream->good() && NeedToFreeInStream) { - ifstream *inFstream = dynamic_cast(pInStream); + if (!success && !pInput->good() && ownedInStreams.size() > 0) { + ifstream *inFstream = dynamic_cast(ownedInStreams[0]); if (inFstream != 0) inFstream->close(); // We will free the stream later, but close the file now } @@ -806,21 +814,6 @@ namespace OpenBabel { return success; } - void OBConversion::InstallStreamFilter() - { - //Do not install filtering input stream if a binary or XML format - //or if already installed in the current InStream (which may have changed). - //Deleting any old LErdbuf before contructing a new one ensures there is - //only one for each OBConversion object. It is deleted in the destructor. - - if(pInFormat && !(pInFormat->Flags() & (READBINARY | READXML)) && pInStream->rdbuf()!=pLineEndBuf) - { - delete pLineEndBuf; - pLineEndBuf = NULL; - pLineEndBuf = new LErdbuf(pInStream->rdbuf()); - pInStream->rdbuf(pLineEndBuf); - } - } ////////////////////////////////////////////////// /// Writes the object pOb but does not delete it afterwards. @@ -828,58 +821,80 @@ namespace OpenBabel { /// Returns true if successful. bool OBConversion::Write(OBBase* pOb, ostream* pos) { - if(pos) pOutStream=pos; + if(pos) SetOutStream(pos, false); - if(!pOutFormat || !pOutStream) return false; + if(!pOutFormat || !pOutput) return false; - ostream* pOrigOutStream = pOutStream; -#ifdef HAVE_LIBZ -#ifndef DISABLE_WRITE_COMPRESSION - zlib_stream::zip_ostream zOut(*pOutStream); - if(IsOption("z",GENOPTIONS)) - { - // make sure to output the header - zOut.make_gzip(); - pOutStream = &zOut; - } -#endif -#endif SetOneObjectOnly(); //So that IsLast() returns true, which is important for XML formats // Set the locale for number parsing to avoid locale issues: PR#1785463 obLocale.SetLocale(); // Also set the C++ stream locale - locale originalLocale = pOutStream->getloc(); // save the original + locale originalLocale = pOutput->getloc(); // save the original locale cNumericLocale(originalLocale, "C", locale::numeric); - pOutStream->imbue(cNumericLocale); + pOutput->imbue(cNumericLocale); // The actual work is done here bool success = pOutFormat->WriteMolecule(pOb,this); - pOutStream = pOrigOutStream; // return the C locale to the original one obLocale.RestoreLocale(); // Restore the C++ stream locale too - pOutStream->imbue(originalLocale); + pOutput->imbue(originalLocale); return success; } + //save the current output state to this streamstate and clear conv + void OBConversion::StreamState::pushOutput(OBConversion& conv) + { + assert(ownedStreams.size() == 0); //should be empty + + pStream = conv.pOutput; + std::copy(conv.ownedOutStreams.begin(), conv.ownedOutStreams.end(), std::back_inserter(ownedStreams)); + + conv.pOutput = NULL; + conv.ownedOutStreams.clear(); + } + + //restore state, blowing away whatever is in conv + void OBConversion::StreamState::popOutput(OBConversion& conv) + { + conv.SetOutStream(NULL); + conv.pOutput = dynamic_cast(pStream); + + assert(conv.ownedOutStreams.size() == 0); //should be empty + + for(unsigned i = 0, n = conv.ownedOutStreams.size(); i < n; i++) + { + std::ostream *s = dynamic_cast(conv.ownedOutStreams[i]); + assert(s); + conv.ownedOutStreams.push_back(s); + } + + pStream = NULL; + ownedStreams.clear(); + } + ////////////////////////////////////////////////// /// Writes the object pOb but does not delete it afterwards. /// The output stream not changed (since we cannot write to this string later) /// Returns true if successful. std::string OBConversion::WriteString(OBBase* pOb, bool trimWhitespace) { - ostream *oldStream = pOutStream; // save old output stringstream newStream; string temp; if(pOutFormat) - { - Write(pOb, &newStream); - } - pOutStream = oldStream; + { + StreamState savedOut; + savedOut.pushOutput(*this); + + SetOutStream(&newStream, false); + Write(pOb); + + savedOut.popOutput(*this); + } temp = newStream.str(); if (trimWhitespace) // trim the trailing whitespace @@ -896,114 +911,91 @@ namespace OpenBabel { /// Returns true if successful. bool OBConversion::WriteFile(OBBase* pOb, string filePath) { - if(!pOutFormat) return false; - - // if we have an old stream, free this first before creating a new one - if (pOutStream && NeedToFreeOutStream) { - delete pOutStream; + if(!pOutFormat) + { + //attempt to autodetect format + pOutFormat = FormatFromExt(filePath.c_str()); + if(!pOutFormat) + return false; } - ofstream *ofs = new ofstream; - NeedToFreeOutStream = true; // make sure we clean this up later - ios_base::openmode omode = - pOutFormat->Flags() & WRITEBINARY ? ios_base::out|ios_base::binary : ios_base::out; - - ofs->open(filePath.c_str(),omode); + ios_base::openmode omode = ios_base::out|ios_base::binary; + ofstream *ofs = new ofstream(filePath.c_str(),omode); if(!ofs || !ofs->good()) { + if(ofs) delete ofs; obErrorLog.ThrowError(__FUNCTION__,"Cannot write to " + filePath, obError); return false; } - return Write(pOb, ofs); + SetOutStream(ofs, true); + return Write(pOb); } void OBConversion::CloseOutFile() { - if (pOutStream && NeedToFreeOutStream) - { - delete pOutStream; - NeedToFreeOutStream = false; - pOutStream = NULL; - } + SetOutStream(NULL); } //////////////////////////////////////////// bool OBConversion::ReadString(OBBase* pOb, std::string input) { - // if we have an old stream, free this first before creating a new one - if (pInStream && NeedToFreeInStream) { - delete pInStream; - } - - stringstream *pin = new stringstream(input); - NeedToFreeInStream = true; // make sure we clean this up later - return Read(pOb, pin); + SetInStream(new stringstream(input), true); + return Read(pOb); } //////////////////////////////////////////// bool OBConversion::ReadFile(OBBase* pOb, std::string filePath) { - if(!pInFormat) return false; + if(!pInFormat) + { + //attempt to auto-detect file format from extension + pInFormat = FormatFromExt(filePath.c_str()); + if(!pInFormat) + return false; + } // save the filename InFilename = filePath; - - // if we have an old stream, free this first before creating a new one - if (pInStream && NeedToFreeInStream) { - delete pInStream; - } - - ifstream *ifs = new ifstream; - NeedToFreeInStream = true; // make sure we free this - ios_base::openmode imode = ios_base::in|ios_base::binary; //now always binary because may be gzipped -// pInFormat->Flags() & READBINARY ? ios_base::in|ios_base::binary : ios_base::in; - - ifs->open(filePath.c_str(),imode); + ifstream *ifs = new ifstream(filePath.c_str(),imode); if(!ifs || !ifs->good()) - { + { + if(ifs) delete ifs; obErrorLog.ThrowError(__FUNCTION__,"Cannot read from " + filePath, obError); return false; - } + } - return Read(pOb,ifs); + SetInStream(ifs, true); + return Read(pOb); } //////////////////////////////////////////// bool OBConversion::OpenInAndOutFiles(std::string infilepath, std::string outfilepath) { - // if we have an old input stream, free this first before creating a new one - if (pInStream && NeedToFreeInStream) - delete pInStream; - - // if we have an old output stream, free this first before creating a new one - if (pOutStream && NeedToFreeOutStream) - delete pOutStream; - - ifstream *ifs = new ifstream; - NeedToFreeInStream = true; // make sure we free this - ifs->open(infilepath.c_str(),ios_base::in|ios_base::binary); //always open in binary mode + ifstream *ifs = new ifstream(infilepath.c_str(),ios_base::in|ios_base::binary); //always open in binary mode if(!ifs || !ifs->good()) { + if(ifs) delete ifs; obErrorLog.ThrowError(__FUNCTION__,"Cannot read from " + infilepath, obError); return false; } - pInStream = ifs; + SetInStream(ifs, true); InFilename = infilepath; if(outfilepath.empty())//Don't open an outfile with an empty name. return true; - ofstream *ofs = new ofstream; - NeedToFreeOutStream = true; // make sure we clean this up later - ofs->open(outfilepath.c_str(),ios_base::out|ios_base::binary);//always open in binary mode + + ofstream *ofs = new ofstream(outfilepath.c_str(),ios_base::out|ios_base::binary);//always open in binary mode if(!ofs || !ofs->good()) { + if(ofs) delete ofs; obErrorLog.ThrowError(__FUNCTION__,"Cannot write to " + outfilepath, obError); return false; } - pOutStream = ofs; + SetOutStream(ofs, true); + OutFilename = outfilepath; return true; } @@ -1017,10 +1009,8 @@ namespace OpenBabel { "-l <#> End import at molecule # specified\n" "-e Continue with next object after error, if possible\n" #ifdef HAVE_LIBZ - #ifndef DISABLE_WRITE_COMPRESSION //Unsolved problem with compression under Windows "-z Compress the output with gzip\n" #endif - #endif "-k Attempt to translate keywords\n"; // -t All input files describe a single molecule } @@ -1165,8 +1155,7 @@ namespace OpenBabel { int Count=0; SetFirstInput(); bool CommonInFormat = pInFormat ? true:false; //whether set in calling routine - ios_base::openmode omode = - pOutFormat->Flags() & WRITEBINARY ? ios_base::out|ios_base::binary : ios_base::out; + ios_base::openmode omode = ios_base::out|ios_base::binary; obErrorLog.ClearLog(); #ifndef DONT_CATCH_EXCEPTIONS try @@ -1327,10 +1316,14 @@ namespace OpenBabel { //Output is put in a temporary stream and written to a file //with an augmenting name only when it contains a valid object. int Indx=1; - SetInStream(&is); -#ifdef HAVE_LIBZ - zlib_stream::zip_istream zIn(is); -#endif + + if(pInFormat && LooksLikeGZip(pIs)) + { + //for backwards compat, attempt to autodetect gzip + pInFormat->SetCompression(OBFormat::GZIP); + } + SetInStream(pIs); + for(;;) { stringstream ss; @@ -1338,13 +1331,6 @@ namespace OpenBabel { SetOutputIndex(0); //reset for new file SetOneObjectOnly(); -#ifdef HAVE_LIBZ - if(Indx==1 && zIn.is_gzip()) { - SetInStream(&zIn); - CheckedForGzip = true; // we know this one is gzip'ed - } -#endif - int ThisFileCount = Convert(); if(ThisFileCount==0) break; Count+=ThisFileCount; @@ -1359,20 +1345,9 @@ namespace OpenBabel { } OutputFileList.push_back(incrfile); -#ifdef HAVE_LIBZ -#ifndef DISABLE_WRITE_COMPRESSION - if(IsOption("z",GENOPTIONS)) - { - zlib_stream::zip_ostream zOut(ofs); - // make sure to output the header - zOut.make_gzip(); - zOut << ss.rdbuf(); - } - else -#endif -#endif - ofs << ss.rdbuf(); - + SetOutStream(&ofs, false); //pickup possible gzip + *pOutput << ss.rdbuf(); + SetOutStream(NULL); ofs.close(); ss.clear(); } @@ -1393,7 +1368,9 @@ namespace OpenBabel { obErrorLog.ThrowError(__FUNCTION__,"Cannot write to " + OutputFileName, obError); return Count; } - os << ssOut.rdbuf(); + SetOutStream(&os, false); + *pOutput << ssOut.rdbuf(); + SetOutStream(NULL); } return Count; } @@ -1437,16 +1414,7 @@ namespace OpenBabel { } } -#ifndef ALL_READS_BINARY - #define ALL_READS_BINARY //now the default -#endif - ios_base::openmode imode; -#ifdef ALL_READS_BINARY //Makes unix files compatible with VC++6 - imode = ios_base::in|ios_base::binary; -#else - imode = pInFormat->Flags() & READBINARY ? ios_base::in|ios_base::binary : ios_base::in; -#endif - + ios_base::openmode imode = ios_base::in|ios_base::binary; is->open(InFilename.c_str(), imode); if(!is->good()) { @@ -1714,6 +1682,26 @@ Additional options : */ + //Return true if stream has magic numbers of a gzipped file + bool OBConversion::LooksLikeGZip(std::istream *pIn) const + { + //only do two get/ungets if necessary + bool isgzip = false; + int c1 = pIn->get(); + if (c1 == 0x1f) + { + int c2 = pIn->get(); + if(c2 == 0x8b) + { + //we have a gzipped file + isgzip = true; + } + pIn->unget(); //unget second magic number + } + pIn->unget(); //first byte didn't match magic number + return isgzip; + } + }//namespace OpenBabel //! \file obconversion.cpp diff --git a/src/obmolecformat.cpp b/src/obmolecformat.cpp index 2d9677de35..115576a5c3 100644 --- a/src/obmolecformat.cpp +++ b/src/obmolecformat.cpp @@ -31,8 +31,8 @@ namespace OpenBabel bool OBMoleculeFormat::ReadChemObjectImpl(OBConversion* pConv, OBFormat* pFormat) { - std::istream &ifs = *pConv->GetInStream(); - if (!ifs.good()) + std::istream *ifs = pConv->GetInStream(); + if (!ifs || !ifs->good()) return false; OBMol* pmol = new OBMol; From b7793d06c8c7b3d49736a12ec3ae2f9626693e49 Mon Sep 17 00:00:00 2001 From: dkoes Date: Wed, 10 Jun 2015 23:46:05 -0400 Subject: [PATCH 02/20] this is intensely frustrating --- CMakeLists.txt | 14 +++-- include/openbabel/format.h | 4 -- include/openbabel/obconversion.h | 21 +++++--- src/CMakeLists.txt | 6 +-- src/obconversion.cpp | 93 ++++++++++++++++---------------- 5 files changed, 73 insertions(+), 65 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0212c08571..da17fc61ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,12 +166,16 @@ if(NOT LIBXML2_FOUND) message(WARNING "libxml2 not found - disabling CML support!") endif() -find_package(Boost 1.36.0 COMPONENTS iostreams) -if(Boost_FOUND) - include_directories(${Boost_INCLUDE_DIRS}) +find_package(ZLIB) +if(ZLIB_FOUND) + add_definitions(-DHAVE_LIBZ) + include_directories(${ZLIB_INCLUDE_DIR}) + # Longstanding unsolved problem with compression under Windows + if(WIN32) + add_definitions(-DDISABLE_WRITE_COMPRESSION) + endif() endif() - # wxWidgets instructions based on http://wiki.wxwidgets.org/CMake #find_package(wxWidgets COMPONENTS base core REQUIRED) find_package(wxWidgets COMPONENTS base core adv) @@ -413,7 +417,7 @@ endif() if(CMAKE_COMPILER_IS_GNUCXX) set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -DNDEBUG") set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O2 -g") - set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -fno-inline") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG -g3 -fno-inline") endif() file(GLOB_RECURSE headers include/openbabel/*.h) diff --git a/include/openbabel/format.h b/include/openbabel/format.h index a803e26180..794d748f6b 100644 --- a/include/openbabel/format.h +++ b/include/openbabel/format.h @@ -148,9 +148,6 @@ std::cerr << "Not a valid input format"; return false;} static OBFormat* FormatFromMIME(const char* MIME); - enum CompressionType {NONE,GZIP}; //someday we will support bzip2 - virtual CompressionType GetCompression() const { return compression; } - virtual void SetCompression(CompressionType val) { compression = val; } private: static PluginMapType &FormatsMIMEMap() { @@ -159,7 +156,6 @@ std::cerr << "Not a valid input format"; return false;} } const char* pMime; - CompressionType compression; /* Functions provided by the MAKE_PLUGIN macro ///Constructor that registers the ID of the format diff --git a/include/openbabel/obconversion.h b/include/openbabel/obconversion.h index 014d5aa3ef..16c6582992 100644 --- a/include/openbabel/obconversion.h +++ b/include/openbabel/obconversion.h @@ -79,9 +79,11 @@ namespace OpenBabel { static OBFormat* FindFormat(const std::string ID); /// @brief Searches registered formats for an ID the same as the file extension static OBFormat* FormatFromExt(const char* filename); + static OBFormat* FormatFromExt(const char* filename, bool& isgzip); /// @brief Searches registered formats for an ID the same as the file extension /// \since version 2.3 static OBFormat* FormatFromExt(const std::string filename); + static OBFormat* FormatFromExt(const std::string filename, bool& isgzip); /// @brief Searches registered formats for a MIME the same as the chemical MIME type passed static OBFormat* FormatFromMIME(const char* MIME); @@ -103,18 +105,20 @@ namespace OpenBabel { std::ostream* GetOutStream() const {return pOutput;}; /// @brief Set input stream. If takeOwnership is true, will deallocate when done. + /// If isGzipped is true, will treat as a gzipped stream regardless of option settings, + // if false, then will be treated as gzipped stream only if z/zin is set. void SetInStream(std::istream* pIn, bool takeOwnership=false); void SetOutStream(std::ostream* pOut, bool takeOwnership=false); /// Sets the formats from their ids, e g CML - bool SetInAndOutFormats(const char* inID, const char* outID); - bool SetInAndOutFormats(OBFormat* pIn, OBFormat* pOut); + bool SetInAndOutFormats(const char* inID, const char* outID, bool ingzip=false, bool outgzip=false); + bool SetInAndOutFormats(OBFormat* pIn, OBFormat* pOut, bool ingzip=false, bool outgzip=false); /// Sets the input format from an id e.g. CML - bool SetInFormat(const char* inID); - bool SetInFormat(OBFormat* pIn); + bool SetInFormat(const char* inID, bool isgzip=false); + bool SetInFormat(OBFormat* pIn, bool isgzip=false); /// Sets the output format from an id e.g. CML - bool SetOutFormat(const char* outID); - bool SetOutFormat(OBFormat* pOut); + bool SetOutFormat(const char* outID, bool isgzip=false); + bool SetOutFormat(OBFormat* pOut, bool isgzip=false); OBFormat* GetInFormat() const{return pInFormat;}; OBFormat* GetOutFormat() const{return pOutFormat;}; @@ -357,7 +361,6 @@ namespace OpenBabel { typedef std::map OPAMapType; static OPAMapType& OptionParamArray(Option_type typ); bool OpenAndSetFormat(bool SetFormat, std::ifstream* is, std::stringstream* ss=NULL); - bool LooksLikeGZip(std::istream *pIn) const; std::string InFilename, OutFilename; //OutFileName added v2.4.0 @@ -387,6 +390,10 @@ namespace OpenBabel { bool ReadyToInput; bool SkippedMolecules; /// skip molecules using -f and -l + //these are set to true if there are indications that a stream is gzipped (e.g., .gz extension) + //unlike the z and zin options, these are not sticky - setting formats or reading/writing different streams will reset them + bool gzipInDetected; + bool gzipOutDetected; OBBase* pOb1; std::streampos wInpos; ///GetCompression() != OBFormat::NONE) + if(IsOption("zin", GENOPTIONS) || gzipInDetected) { zlib_stream::zip_istream *zIn = new zlib_stream::zip_istream(*pInput); ownedInStreams.push_back(zIn); @@ -363,10 +364,9 @@ namespace OpenBabel { #ifdef HAVE_LIBZ - if ( (pOutFormat && pOutFormat->GetCompression() != OBFormat::NONE) || - IsOption("z", GENOPTIONS)) + if (IsOption("z", GENOPTIONS) || gzipOutDetected) { - zlib_stream::zip_ostream *zOut = new zlib_stream::zip_ostream(*pOutput); + zlib_stream::zip_ostream *zOut = new zlib_stream::zip_ostream(*pOutput, true); ownedOutStreams.push_back(zOut); pOutput = zOut; } @@ -380,41 +380,45 @@ namespace OpenBabel { /// Sets the formats from their ids, e g CML. /// If inID is NULL, the input format is left unchanged. Similarly for outID /// Returns true if both formats have been successfully set at sometime - bool OBConversion::SetInAndOutFormats(const char* inID, const char* outID) + bool OBConversion::SetInAndOutFormats(const char* inID, const char* outID, bool inzip, bool outzip) { - return SetInFormat(inID) && SetOutFormat(outID); + return SetInFormat(inID, inzip) && SetOutFormat(outID, outzip); } ////////////////////////////////////////////////////// - bool OBConversion::SetInAndOutFormats(OBFormat* pIn, OBFormat* pOut) + bool OBConversion::SetInAndOutFormats(OBFormat* pIn, OBFormat* pOut, bool inzip, bool outzip) { - return SetInFormat(pIn) && SetOutFormat(pOut); + return SetInFormat(pIn, inzip) && SetOutFormat(pOut, outzip); } ////////////////////////////////////////////////////// - bool OBConversion::SetInFormat(OBFormat* pIn) + bool OBConversion::SetInFormat(OBFormat* pIn, bool gzip) { + gzipInDetected = gzip; if(pIn==NULL) return true; pInFormat=pIn; return !(pInFormat->Flags() & NOTREADABLE); } ////////////////////////////////////////////////////// - bool OBConversion::SetOutFormat(OBFormat* pOut) + bool OBConversion::SetOutFormat(OBFormat* pOut, bool gzip) { + gzipOutDetected = gzip; pOutFormat=pOut; return pOut && !(pOutFormat->Flags() & NOTWRITABLE); } ////////////////////////////////////////////////////// - bool OBConversion::SetInFormat(const char* inID) + bool OBConversion::SetInFormat(const char* inID, bool gzip) { + gzipInDetected = gzip; if(inID) pInFormat = FindFormat(inID); return pInFormat && !(pInFormat->Flags() & NOTREADABLE); } ////////////////////////////////////////////////////// - bool OBConversion::SetOutFormat(const char* outID) + bool OBConversion::SetOutFormat(const char* outID, bool gzip) { + gzipOutDetected = gzip; if(outID) pOutFormat= FindFormat(outID); return pOutFormat && !(pOutFormat->Flags() & NOTWRITABLE); @@ -719,24 +723,23 @@ namespace OpenBabel { } ///////////////////////////////////////////////////////// - OBFormat* OBConversion::FormatFromExt(const char* filename) + OBFormat* OBConversion::FormatFromExt(const char* filename, bool& isgzip) { string file = filename; string::size_type extPos = file.rfind('.'); - + isgzip = false; if(extPos!=string::npos // period found && (file.substr(extPos + 1, file.size())).find("/")==string::npos) // and period is after the last "/" { // only do this if we actually can read .gz files if (file.substr(extPos) == ".gz") - { + { + isgzip = true; file.erase(extPos); extPos = file.rfind('.'); if (extPos!=string::npos) { - OBFormat *ret = FindFormat( (file.substr(extPos + 1, file.size())).c_str() ); - if(ret) ret->SetCompression(OBFormat::GZIP); - return ret; + return FindFormat( (file.substr(extPos + 1, file.size())).c_str() ); } } else @@ -752,9 +755,21 @@ namespace OpenBabel { return FindFormat( file.c_str() ); //if no format found } + OBFormat* OBConversion::FormatFromExt(const char* filename) + { + bool isgzip; + FormatFromExt(filename, isgzip); + } + OBFormat* OBConversion::FormatFromExt(const std::string filename) { - return FormatFromExt(filename.c_str()); + bool gzip; + return FormatFromExt(filename.c_str(), gzip); + } + + OBFormat* OBConversion::FormatFromExt(const std::string filename, bool& isgzip) + { + return FormatFromExt(filename.c_str(), isgzip); } OBFormat* OBConversion::FormatFromMIME(const char* MIME) @@ -766,11 +781,13 @@ namespace OpenBabel { { if(pin) { //for backwards compatibility, attempt to detect a gzip file - if(pInFormat && LooksLikeGZip(pin)) + if(pInFormat && zlib_stream::isGZip(*pin)) { - pInFormat->SetCompression(OBFormat::GZIP); + gzipInDetected = true; } - SetInStream(pin, false); + + SetInStream(pin, false); + } if(!pInFormat || !pInput) return false; @@ -911,10 +928,11 @@ namespace OpenBabel { /// Returns true if successful. bool OBConversion::WriteFile(OBBase* pOb, string filePath) { + bool isgzip = false; if(!pOutFormat) { //attempt to autodetect format - pOutFormat = FormatFromExt(filePath.c_str()); + pOutFormat = FormatFromExt(filePath.c_str(), gzipOutDetected); if(!pOutFormat) return false; } @@ -951,7 +969,7 @@ namespace OpenBabel { if(!pInFormat) { //attempt to auto-detect file format from extension - pInFormat = FormatFromExt(filePath.c_str()); + pInFormat = FormatFromExt(filePath.c_str(), gzipInDetected); if(!pInFormat) return false; } @@ -1010,6 +1028,7 @@ namespace OpenBabel { "-e Continue with next object after error, if possible\n" #ifdef HAVE_LIBZ "-z Compress the output with gzip\n" + "-zin Decompress the input with gzip\n" #endif "-k Attempt to translate keywords\n"; // -t All input files describe a single molecule @@ -1317,12 +1336,13 @@ namespace OpenBabel { //with an augmenting name only when it contains a valid object. int Indx=1; - if(pInFormat && LooksLikeGZip(pIs)) + if(pInFormat && zlib_stream::isGZip(*pIs)) { //for backwards compat, attempt to autodetect gzip - pInFormat->SetCompression(OBFormat::GZIP); + gzipInDetected = true; } - SetInStream(pIs); + SetInStream(pIs, false); + for(;;) { @@ -1682,25 +1702,6 @@ Additional options : */ - //Return true if stream has magic numbers of a gzipped file - bool OBConversion::LooksLikeGZip(std::istream *pIn) const - { - //only do two get/ungets if necessary - bool isgzip = false; - int c1 = pIn->get(); - if (c1 == 0x1f) - { - int c2 = pIn->get(); - if(c2 == 0x8b) - { - //we have a gzipped file - isgzip = true; - } - pIn->unget(); //unget second magic number - } - pIn->unget(); //first byte didn't match magic number - return isgzip; - } }//namespace OpenBabel From 04bbfab75a5e0e6fc6e2cb50183f7955c570b111 Mon Sep 17 00:00:00 2001 From: dkoes Date: Thu, 11 Jun 2015 08:15:32 -0400 Subject: [PATCH 03/20] slight reworking of gzip state, babel fix --- include/openbabel/obconversion.h | 7 +++---- src/obconversion.cpp | 22 +++++++++++----------- tools/babel.cpp | 6 ++++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/openbabel/obconversion.h b/include/openbabel/obconversion.h index 16c6582992..b9f3ea64fb 100644 --- a/include/openbabel/obconversion.h +++ b/include/openbabel/obconversion.h @@ -390,10 +390,9 @@ namespace OpenBabel { bool ReadyToInput; bool SkippedMolecules; /// skip molecules using -f and -l - //these are set to true if there are indications that a stream is gzipped (e.g., .gz extension) - //unlike the z and zin options, these are not sticky - setting formats or reading/writing different streams will reset them - bool gzipInDetected; - bool gzipOutDetected; + //unlike the z and zin options, these are not sticky - setting formats will reset them + bool inFormatGzip; + bool outFormatGzip; OBBase* pOb1; std::streampos wInpos; ///Flags() & NOTWRITABLE); } ////////////////////////////////////////////////////// bool OBConversion::SetInFormat(const char* inID, bool gzip) { - gzipInDetected = gzip; + inFormatGzip = gzip; if(inID) pInFormat = FindFormat(inID); return pInFormat && !(pInFormat->Flags() & NOTREADABLE); @@ -418,7 +418,7 @@ namespace OpenBabel { bool OBConversion::SetOutFormat(const char* outID, bool gzip) { - gzipOutDetected = gzip; + outFormatGzip = gzip; if(outID) pOutFormat= FindFormat(outID); return pOutFormat && !(pOutFormat->Flags() & NOTWRITABLE); @@ -783,7 +783,7 @@ namespace OpenBabel { //for backwards compatibility, attempt to detect a gzip file if(pInFormat && zlib_stream::isGZip(*pin)) { - gzipInDetected = true; + inFormatGzip = true; } SetInStream(pin, false); @@ -932,7 +932,7 @@ namespace OpenBabel { if(!pOutFormat) { //attempt to autodetect format - pOutFormat = FormatFromExt(filePath.c_str(), gzipOutDetected); + pOutFormat = FormatFromExt(filePath.c_str(), outFormatGzip); if(!pOutFormat) return false; } @@ -969,7 +969,7 @@ namespace OpenBabel { if(!pInFormat) { //attempt to auto-detect file format from extension - pInFormat = FormatFromExt(filePath.c_str(), gzipInDetected); + pInFormat = FormatFromExt(filePath.c_str(), inFormatGzip); if(!pInFormat) return false; } @@ -1339,7 +1339,7 @@ namespace OpenBabel { if(pInFormat && zlib_stream::isGZip(*pIs)) { //for backwards compat, attempt to autodetect gzip - gzipInDetected = true; + inFormatGzip = true; } SetInStream(pIs, false); diff --git a/tools/babel.cpp b/tools/babel.cpp index bc9fb38ae9..049c24d51e 100644 --- a/tools/babel.cpp +++ b/tools/babel.cpp @@ -61,6 +61,8 @@ int main(int argc,char *argv[]) OBFormat* pInFormat = NULL; OBFormat* pOutFormat = NULL; + bool inGzip = false; + bool outGzip = false; vector FileList, OutputFileList; string OutputFileName; @@ -305,7 +307,7 @@ int main(int argc,char *argv[]) if (!gotOutType) { - pOutFormat = Conv.FormatFromExt(OutputFileName.c_str()); + pOutFormat = Conv.FormatFromExt(OutputFileName.c_str(), outGzip); if(pOutFormat==NULL) { cerr << program_name << ": cannot write output format!" << endl; @@ -318,7 +320,7 @@ int main(int argc,char *argv[]) cerr << "Invalid input format" << endl; usage(); } - if(!Conv.SetOutFormat(pOutFormat)) + if(!Conv.SetOutFormat(pOutFormat, outGzip)) { cerr << "Invalid output format" << endl; usage(); From bd7719c5a123a6ac6d934572f3086e5b7835512e Mon Sep 17 00:00:00 2001 From: David Koes Date: Thu, 11 Jun 2015 17:01:50 -0400 Subject: [PATCH 04/20] bug fixes --- include/openbabel/obconversion.h | 5 +++ src/formats/xml/xml.cpp | 1 + src/obconversion.cpp | 67 ++++++++++++++++++++++++++++---- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/include/openbabel/obconversion.h b/include/openbabel/obconversion.h index b9f3ea64fb..00f9dd9ba0 100644 --- a/include/openbabel/obconversion.h +++ b/include/openbabel/obconversion.h @@ -351,8 +351,13 @@ namespace OpenBabel { assert(ownedStreams.size() == 0); //should be popped } + void pushInput(OBConversion& conv); + void popInput(OBConversion& conv); + void pushOutput(OBConversion& conv); void popOutput(OBConversion& conv); + + bool isSet() const { return pStream != NULL; } }; bool SetStartAndEnd(); diff --git a/src/formats/xml/xml.cpp b/src/formats/xml/xml.cpp index ffd18ff3e8..81c6450dd4 100644 --- a/src/formats/xml/xml.cpp +++ b/src/formats/xml/xml.cpp @@ -326,6 +326,7 @@ namespace OpenBabel //@todo worry about non-ascii coding XMLConversion* pConv = static_cast(context); istream* ifs = pConv->GetInStream(); + if(!ifs->good() || ifs->eof()) return 0; diff --git a/src/obconversion.cpp b/src/obconversion.cpp index 7e20a55aa3..d85579dad5 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -254,6 +254,10 @@ namespace OpenBabel { //the original obconversion retains ownership of any allocated streams //this means if the original gets destroyed, bad things may happen //by doing this first, format isn't initialized, so we add no additional filters + pInFormat = NULL; + inFormatGzip = false; + pOutFormat = NULL; + outFormatGzip = false; SetInStream(o.pInput, false); SetOutStream(o.pOutput, false); @@ -262,7 +266,9 @@ namespace OpenBabel { StartNumber = o.StartNumber; EndNumber = o.EndNumber; pInFormat = o.pInFormat; + inFormatGzip = o.inFormatGzip; pOutFormat = o.pOutFormat; + outFormatGzip = o.outFormatGzip; OptionsArray[0]= o.OptionsArray[0]; OptionsArray[1]= o.OptionsArray[1]; OptionsArray[2]= o.OptionsArray[2]; @@ -346,6 +352,9 @@ namespace OpenBabel { /// Set output stream, removing/deallocating previous stream if necessary. /// If takeOwnership is true, takes responsibility for freeing pOut + /// Be aware that if the output stream is gzipped format, then this outstream + /// either needs to be replaced (e.g., SetOutStream(NULL)) or the OBConversion + /// destroyed before the underlying outputstream is deallocated. void OBConversion::SetOutStream(std::ostream* pOut, bool takeOwnership) { //clear and deallocate any existing streams @@ -367,7 +376,8 @@ namespace OpenBabel { if (IsOption("z", GENOPTIONS) || outFormatGzip) { zlib_stream::zip_ostream *zOut = new zlib_stream::zip_ostream(*pOutput, true); - ownedOutStreams.push_back(zOut); + //we need to delete the zstream _before_ the underlying stream so it can add the footer + ownedOutStreams.insert(ownedOutStreams.begin(),zOut); pOutput = zOut; } #endif @@ -426,13 +436,27 @@ namespace OpenBabel { ////////////////////////////////////////////////////// /// Convert molecules from is into os. If either is null, uses existing streams. - /// Unlike other methods that take a stream, if a stream is specified, it does _not_ replace the existing stream. + /// If streams are specified, they do _not_ replace any existing streams. int OBConversion::Convert(istream* is, ostream* os) { - if (is) SetInStream(is, false); - if (os) SetOutStream(os, false); + StreamState savedIn, savedOut; + if (is) + { + savedIn.pushInput(*this); + SetInStream(is, false); + } + + if (os) + { + savedOut.pushOutput(*this); + SetOutStream(os, false); + } int count = Convert(); + + if(savedIn.isSet()) savedIn.popInput(*this); + if(savedOut.isSet()) savedOut.popOutput(*this); + return count; } @@ -785,9 +809,7 @@ namespace OpenBabel { { inFormatGzip = true; } - - SetInStream(pin, false); - + SetInStream(pin, false); } if(!pInFormat || !pInput) return false; @@ -862,6 +884,37 @@ namespace OpenBabel { return success; } + //save the current input state to this streamstate and clear conv + void OBConversion::StreamState::pushInput(OBConversion& conv) + { + assert(ownedStreams.size() == 0); //should be empty + + pStream = conv.pInput; + std::copy(conv.ownedInStreams.begin(), conv.ownedInStreams.end(), std::back_inserter(ownedStreams)); + + conv.pInput = NULL; + conv.ownedInStreams.clear(); + } + + //restore state, blowing away whatever is in conv + void OBConversion::StreamState::popInput(OBConversion& conv) + { + conv.SetInStream(NULL); + conv.pInput = dynamic_cast(pStream); + + assert(conv.ownedInStreams.size() == 0); //should be empty + + for(unsigned i = 0, n = conv.ownedInStreams.size(); i < n; i++) + { + std::istream *s = dynamic_cast(conv.ownedInStreams[i]); + assert(s); + conv.ownedInStreams.push_back(s); + } + + pStream = NULL; + ownedStreams.clear(); + } + //save the current output state to this streamstate and clear conv void OBConversion::StreamState::pushOutput(OBConversion& conv) { From 2c90acb23082426105d93665ac9522585bc4a50f Mon Sep 17 00:00:00 2001 From: dkoes Date: Fri, 12 Jun 2015 00:17:15 -0400 Subject: [PATCH 05/20] "fix" eof/bad iostream behavior --- src/obconversion.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index d85579dad5..3a5cbdb01d 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -812,8 +812,15 @@ namespace OpenBabel { SetInStream(pin, false); } + if(!pInFormat || !pInput) return false; + //mysterious line to ensure backwards compatibility + //previously, even an open istream would have the gzip check applied + //this meant that a stream at the eof position would end up in an error state + //code has come to depend on this behavior + if(pInput->eof()) pInput->get(); + // Set the locale for number parsing to avoid locale issues: PR#1785463 obLocale.SetLocale(); From f93ea8817a4ae34e15c9e3de8b6801812a5e408d Mon Sep 17 00:00:00 2001 From: dkoes Date: Fri, 12 Jun 2015 00:22:34 -0400 Subject: [PATCH 06/20] switch lineend stream to work on stream, not streambuf this way we are not retaining a pointer to the underlying streambuf, just the user provided stream --- include/openbabel/lineend.h | 45 ++++++++++++++++++------------------- test/charge_mmff94.cpp | 2 +- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/include/openbabel/lineend.h b/include/openbabel/lineend.h index f701e1261e..cc3fe99b7d 100644 --- a/include/openbabel/lineend.h +++ b/include/openbabel/lineend.h @@ -60,9 +60,7 @@ namespace OpenBabel { public: FilteringInputStreambuf( - std::streambuf* source = NULL , - bool deleteWhenFinished = false - ) ; + std::istream* source = NULL) ; virtual ~FilteringInputStreambuf() { //sync(); comment out so can be deleted in OBConversion destructor @@ -75,7 +73,9 @@ namespace OpenBabel virtual std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out ) { - std::streampos ret = mySource->pubseekoff(off, way, which); + setg( NULL , NULL , NULL ) ; //ensure next character is from new position + mySource->seekg(off, way); + std::streampos ret = mySource->tellg(); // sync(); return ret; }; @@ -83,19 +83,21 @@ namespace OpenBabel virtual std::streampos seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out ) { - std::streampos ret = mySource->pubseekpos(sp, which); + setg( NULL , NULL , NULL ) ; + mySource->seekg(sp); + std::streampos ret = mySource->tellg(); // sync(); return ret; }; /// Returns current source. - std::streambuf* GetSource()const + std::istream* GetSource()const { return mySource; }; ///Changes the source - void SetSource(std::streambuf* newsource) + void SetSource(std::istream* newsource) { mySource = newsource; setg( &myBuffer , &myBuffer , &myBuffer + 1 ) ; @@ -104,18 +106,16 @@ namespace OpenBabel // Extractor& extractor() {return myExtractor;}; private: - std::streambuf* mySource ; + std::istream* mySource ; Extractor myExtractor ; char myBuffer ; - bool myDeleteWhenFinished ; } ; //******************************************************* template< class Extractor > FilteringInputStreambuf< Extractor >::FilteringInputStreambuf( - std::streambuf* source , - bool deleteWhenFinished) - : mySource(source), myDeleteWhenFinished(deleteWhenFinished) + std::istream* source ) + : mySource(source) { setg( &myBuffer , &myBuffer , &myBuffer ) ; } @@ -151,12 +151,11 @@ namespace OpenBabel if ( mySource != NULL ) { if ( gptr() < egptr() ) - { - result = mySource->sputbackc( *gptr() ) ; - setg( NULL , NULL , NULL ) ; + { //have read something but not provided it + mySource->putback(*gptr() ) ; + setg( &myBuffer , &myBuffer , &myBuffer ) ; } - if ( mySource->pubsync() == EOF ) - result = EOF ; + result = mySource->sync(); } return result ; } @@ -167,14 +166,14 @@ namespace OpenBabel class OBCONV LineEndingExtractor { public: - int operator()( std::streambuf& src ) + int operator()( std::istream& src ) { - int ch( src.sbumpc() ) ; + int ch( src.get() ) ; switch (ch) { case 13: //CR or CRLF - if(src.sgetc() == 10) - src.sbumpc(); //CRLF + if(src.peek() == 10) + src.get(); //CRLF //fall through case 10: //LF return '\n'; @@ -191,7 +190,7 @@ class OBCONV LineEndingExtractor */ template class FilteringInputStream : - public FilteringInputStreambuf, + virtual private FilteringInputStreambuf, public std::istream { public: @@ -199,7 +198,7 @@ class FilteringInputStream : typedef std::istream istream_type; explicit FilteringInputStream(istream_reference istream): - FilteringInputStreambuf(istream.rdbuf()),std::istream(this) {} + FilteringInputStreambuf(&istream),std::istream(this) {} virtual ~FilteringInputStream() {} }; diff --git a/test/charge_mmff94.cpp b/test/charge_mmff94.cpp index 540de05f90..d6d52c5bda 100644 --- a/test/charge_mmff94.cpp +++ b/test/charge_mmff94.cpp @@ -103,7 +103,7 @@ int charge_mmff94(int argc, char* argv[]) mol.Clear(); conv.Read(&mol); if (mol.Empty()) - continue; + continue; //dkoes - this should totally be break if (!difs.getline(buffer,BUFF_SIZE)) { cout << "Bail out! error reading reference data" << endl; From 5c9cadb858c40e4f637639ae5244b5f197508690 Mon Sep 17 00:00:00 2001 From: dkoes Date: Fri, 12 Jun 2015 08:33:47 -0400 Subject: [PATCH 07/20] fix bug with xmlconversion implement assignment operator so ownership of allocated streams remains with one obconversion --- include/openbabel/obconversion.h | 5 ++++- src/obconversion.cpp | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/openbabel/obconversion.h b/include/openbabel/obconversion.h index 00f9dd9ba0..d232ad405c 100644 --- a/include/openbabel/obconversion.h +++ b/include/openbabel/obconversion.h @@ -64,8 +64,11 @@ namespace OpenBabel { /// @name Construction //@{ OBConversion(std::istream* is=NULL, std::ostream* os=NULL); - /// @brief Copy constructor + /// @brief Copy constructor. Stream *ownership* is not copied. Source remains responsible for the memory. OBConversion(const OBConversion& o); + /// @brief Assignment. Stream *ownership* is not copied. Source remains responsible for the memory. + OBConversion& operator=(const OBConversion& rhs); + virtual ~OBConversion(); //@} /// @name Collection of formats diff --git a/src/obconversion.cpp b/src/obconversion.cpp index 3a5cbdb01d..54bedc9227 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -250,6 +250,12 @@ namespace OpenBabel { ///////////////////////////////////////////////// OBConversion::OBConversion(const OBConversion& o) + { + *this = o; + } + + + OBConversion& OBConversion::operator=(const OBConversion& o) { //the original obconversion retains ownership of any allocated streams //this means if the original gets destroyed, bad things may happen From ccec4db7d5be1ef4920f72e8e4d908d7c0ffa1a3 Mon Sep 17 00:00:00 2001 From: dkoes Date: Fri, 12 Jun 2015 18:20:21 -0400 Subject: [PATCH 08/20] fix xml problem need to set auxconv in assignment, this is _not_ a good design pattern --- src/obconversion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index 54bedc9227..c0a7f8c565 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -290,7 +290,7 @@ namespace OpenBabel { ReadyToInput = o.ReadyToInput; m_IsFirstInput = o.m_IsFirstInput; SkippedMolecules = o.SkippedMolecules; - pAuxConv = NULL; + pAuxConv = o.pAuxConv; } /////////////////////////////////////////////// From fe28546be44205b375f905bee5087d3f07b90933 Mon Sep 17 00:00:00 2001 From: dkoes Date: Sat, 13 Jun 2015 12:42:19 -0400 Subject: [PATCH 09/20] another fix clear status on original stream before seeking --- include/openbabel/lineend.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/openbabel/lineend.h b/include/openbabel/lineend.h index cc3fe99b7d..0184cecbc8 100644 --- a/include/openbabel/lineend.h +++ b/include/openbabel/lineend.h @@ -73,7 +73,7 @@ namespace OpenBabel virtual std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out ) { - setg( NULL , NULL , NULL ) ; //ensure next character is from new position + setg( &myBuffer , &myBuffer , &myBuffer ) ; //ensure next character is from new position mySource->seekg(off, way); std::streampos ret = mySource->tellg(); // sync(); @@ -83,7 +83,10 @@ namespace OpenBabel virtual std::streampos seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out ) { - setg( NULL , NULL , NULL ) ; + setg( &myBuffer , &myBuffer , &myBuffer ) ; + //slight hack - if mySource has read past the end, won't let us seek + //and there's no good way to propagate a clear to the original stream + mySource->clear(); mySource->seekg(sp); std::streampos ret = mySource->tellg(); // sync(); From 67616b937dfefe82de1d60bb4c3e33093afa9a7e Mon Sep 17 00:00:00 2001 From: dkoes Date: Sun, 14 Jun 2015 14:02:49 -0400 Subject: [PATCH 10/20] testing gzip support --- include/openbabel/obconversion.h | 5 +++++ src/formats/xml/xml.cpp | 3 ++- src/obconversion.cpp | 33 +++++++++++++++++++++++++++++--- src/zipstreamimpl.h | 13 ++++++++++++- test/CMakeLists.txt | 6 ++++-- 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/include/openbabel/obconversion.h b/include/openbabel/obconversion.h index d232ad405c..1a466742da 100644 --- a/include/openbabel/obconversion.h +++ b/include/openbabel/obconversion.h @@ -64,6 +64,7 @@ namespace OpenBabel { /// @name Construction //@{ OBConversion(std::istream* is=NULL, std::ostream* os=NULL); + OBConversion(std::string inFilename, std::string outFilename=""); /// @brief Copy constructor. Stream *ownership* is not copied. Source remains responsible for the memory. OBConversion(const OBConversion& o); /// @brief Assignment. Stream *ownership* is not copied. Source remains responsible for the memory. @@ -125,6 +126,8 @@ namespace OpenBabel { OBFormat* GetInFormat() const{return pInFormat;}; OBFormat* GetOutFormat() const{return pOutFormat;}; + bool GetInGzipped() const{return inFormatGzip;}; + bool GetOutGzipped() const{return outFormatGzip;}; std::string GetInFilename() const{return InFilename;}; std::string GetOutFilename() const{return OutFilename;}; @@ -314,6 +317,8 @@ namespace OpenBabel { /// Open the files and update the streams in the OBConversion object. /// This method is primarily intended for scripting languages without "stream" classes /// and will usually followed by a call to Convert(). + /// Will set format from file extension if format has not already been set. + /// Files will be opened even if format cannot be determined, but not if file path is empty. /// \return false if unsucessful. bool OpenInAndOutFiles(std::string infilepath, std::string outfilepath); diff --git a/src/formats/xml/xml.cpp b/src/formats/xml/xml.cpp index 81c6450dd4..68ac08a72b 100644 --- a/src/formats/xml/xml.cpp +++ b/src/formats/xml/xml.cpp @@ -145,6 +145,7 @@ namespace OpenBabel if(ForReading) { streampos pos = pConv->GetInStream()->tellg(); + if(pos < pxmlConv->_lastpos || pxmlConv->_lastpos<0) { //Probably a new file; copy some member vars and renew the current reader @@ -182,7 +183,7 @@ namespace OpenBabel //**Parse int result=1; - while(!GetInStream()->bad() && (_SkipNextRead || (result=xmlTextReaderRead(_reader))==1)) //read may not be called + while(!GetInStream()->bad() && !GetInStream()->eof() && (_SkipNextRead || (result=xmlTextReaderRead(_reader))==1)) //read may not be called { _SkipNextRead=false; if(_LookingForNamespace) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index c0a7f8c565..4c24645832 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -248,6 +248,23 @@ namespace OpenBabel { RegisterOptionParam("l", NULL, 1,GENOPTIONS); } + /// Convenience constructor. Sets up streams from specified files. + /// If format can not be determined from filename, a stream is not opened. + OBConversion::OBConversion(string infile, string outfile): + pInput(NULL), pOutput(NULL), + pInFormat(NULL),pOutFormat(NULL), Index(0), StartNumber(1), + EndNumber(0), Count(-1), m_IsFirstInput(true), m_IsLast(true), + MoreFilesToCome(false), OneObjectOnly(false), SkippedMolecules(false), + inFormatGzip(false), outFormatGzip(false), + pOb1(NULL), pAuxConv(NULL),wInpos(0),wInlen(0) + { + //These options take a parameter + RegisterOptionParam("f", NULL, 1,GENOPTIONS); + RegisterOptionParam("l", NULL, 1,GENOPTIONS); + + OpenInAndOutFiles(infile, outfile); + } + ///////////////////////////////////////////////// OBConversion::OBConversion(const OBConversion& o) { @@ -826,7 +843,7 @@ namespace OpenBabel { //this meant that a stream at the eof position would end up in an error state //code has come to depend on this behavior if(pInput->eof()) pInput->get(); - +cout << pInput->tellg() << " " << pInput->eof() << "\n"; // Set the locale for number parsing to avoid locale issues: PR#1785463 obLocale.SetLocale(); @@ -994,7 +1011,6 @@ namespace OpenBabel { /// Returns true if successful. bool OBConversion::WriteFile(OBBase* pOb, string filePath) { - bool isgzip = false; if(!pOutFormat) { //attempt to autodetect format @@ -1058,6 +1074,12 @@ namespace OpenBabel { //////////////////////////////////////////// bool OBConversion::OpenInAndOutFiles(std::string infilepath, std::string outfilepath) { + + if(!pInFormat) + { + //attempt to auto-detect file format from extension + pInFormat = FormatFromExt(infilepath.c_str(), inFormatGzip); + } ifstream *ifs = new ifstream(infilepath.c_str(),ios_base::in|ios_base::binary); //always open in binary mode if(!ifs || !ifs->good()) { @@ -1071,6 +1093,11 @@ namespace OpenBabel { if(outfilepath.empty())//Don't open an outfile with an empty name. return true; + if(!pOutFormat) + { + //attempt to autodetect format + pOutFormat = FormatFromExt(outfilepath.c_str(), outFormatGzip); + } ofstream *ofs = new ofstream(outfilepath.c_str(),ios_base::out|ios_base::binary);//always open in binary mode if(!ofs || !ofs->good()) { @@ -1487,7 +1514,7 @@ namespace OpenBabel { } else if(!SetFormat) { - pInFormat = FormatFromExt(InFilename.c_str()); + pInFormat = FormatFromExt(InFilename.c_str(), inFormatGzip); if(pInFormat==NULL) { string::size_type pos = InFilename.rfind('.'); diff --git a/src/zipstreamimpl.h b/src/zipstreamimpl.h index 3eff644daa..865729e17f 100644 --- a/src/zipstreamimpl.h +++ b/src/zipstreamimpl.h @@ -568,7 +568,18 @@ basic_unzip_streambuf::unzip_from_stream(char_type* buffer, // check if it is the end if (_err == Z_STREAM_END) - put_back_from_zip_stream(); + { //dkoes, support concatenated zip files + put_back_from_zip_stream(); + // we do _not_ reset the zipstream since seeking requires we keep a running total + //read footer + + for(unsigned i = 0; i < 8; i++) + { + get_istream().get(); //but ignore since for some reason check_footer is in the stream class.. and isn't called anyway + } + + _err = check_header(); + } return n_read; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5dcd6f3ecb..4ef815247e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,8 +16,8 @@ add_definitions(-DFORMATDIR="\\"${FORMATDIR}/\\"") ################ Add new tests here set (cpptests automorphism builder canonconsistent canonstable carspacegroup - cistrans graphsym - implicitH lssr isomorphism regressions rotor shuffle smiles spectrophore + cistrans graphsym gzip + implicitH lssr isomorphism multicml regressions rotor shuffle smiles spectrophore squareplanar stereo stereoperception tetrahedral tetranonplanar tetraplanar uniqueid ) @@ -28,9 +28,11 @@ set (canonstable_parts 1) set (carspacegroup_parts 1 2 3 4) set (cistrans_parts 1 2 3 4 5 6 7 8 9) set (graphsym_parts 1 2 3 4 5) +set (gzip_parts 1) set (implicitH_parts 1) set (lssr_parts 1 2 3 4 5) set (isomorphism_parts 1 2 3 4 5 6 7 8) +set (multicml_parts 1) set (regressions_parts 1 221 222) set (rotor_parts 1 2 3 4) set (shuffle_parts 1 2 3 4 5) From 698c1ba30c041a0a45b43d3c4216261d789ebb14 Mon Sep 17 00:00:00 2001 From: dkoes Date: Sun, 14 Jun 2015 15:17:08 -0400 Subject: [PATCH 11/20] fix xml reading at end --- src/formats/xml/xml.cpp | 14 +++++++++++--- src/obconversion.cpp | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/formats/xml/xml.cpp b/src/formats/xml/xml.cpp index 68ac08a72b..1666f0df5d 100644 --- a/src/formats/xml/xml.cpp +++ b/src/formats/xml/xml.cpp @@ -183,6 +183,7 @@ namespace OpenBabel //**Parse int result=1; + unsigned elementCnt = 0; while(!GetInStream()->bad() && !GetInStream()->eof() && (_SkipNextRead || (result=xmlTextReaderRead(_reader))==1)) //read may not be called { _SkipNextRead=false; @@ -204,8 +205,7 @@ namespace OpenBabel _LookingForNamespace=false; _SkipNextRead=true; SetInFormat(pNewFormat); - pNewFormat->ReadMolecule(pOb,this); - return true; + return pNewFormat->ReadMolecule(pOb,this); } } } @@ -220,7 +220,10 @@ namespace OpenBabel //Pass the node on to the appropriate format class bool ret; if(typ==XML_READER_TYPE_ELEMENT) + { + elementCnt++; ret= pFormat->DoElement(ElName); + } else if(typ==XML_READER_TYPE_END_ELEMENT) ret= pFormat->EndElement(ElName); else @@ -235,7 +238,7 @@ namespace OpenBabel _LookingForNamespace = true; return true; } - } + } if(result==-1) { @@ -249,6 +252,11 @@ namespace OpenBabel GetInStream()->setstate(ios::eofbit); return false; } + else if(elementCnt == 0) + { + //didn't actually read any data (e.g., end tag) + return false; + } return GetInStream()->good() && result!=0; } diff --git a/src/obconversion.cpp b/src/obconversion.cpp index 4c24645832..ff81cfa0de 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -843,7 +843,7 @@ namespace OpenBabel { //this meant that a stream at the eof position would end up in an error state //code has come to depend on this behavior if(pInput->eof()) pInput->get(); -cout << pInput->tellg() << " " << pInput->eof() << "\n"; + // Set the locale for number parsing to avoid locale issues: PR#1785463 obLocale.SetLocale(); From d34579511061a09ac7ea873294fa85c3eaff875e Mon Sep 17 00:00:00 2001 From: dkoes Date: Sun, 14 Jun 2015 15:21:05 -0400 Subject: [PATCH 12/20] add test cases --- test/files/1ubq.pdb.gz | Bin 0 -> 18118 bytes test/files/c3.cml | 231 ++++++++++++++++++++++++++++++++++ test/files/c3.cml.gz | Bin 0 -> 1678 bytes test/files/c4.mol2.gz | Bin 0 -> 3051 bytes test/files/c5.smi.gz | Bin 0 -> 154 bytes test/files/five_obabel.sdf.gz | Bin 0 -> 2388 bytes test/files/gzip.in | 37 ++++++ test/files/many.sdf.gz | Bin 0 -> 44743 bytes test/files/tencmpds.sdf.gz | Bin 0 -> 4020 bytes test/gziptest.cpp | 157 +++++++++++++++++++++++ test/multicmltest.cpp | 95 ++++++++++++++ 11 files changed, 520 insertions(+) create mode 100644 test/files/1ubq.pdb.gz create mode 100644 test/files/c3.cml create mode 100644 test/files/c3.cml.gz create mode 100644 test/files/c4.mol2.gz create mode 100644 test/files/c5.smi.gz create mode 100644 test/files/five_obabel.sdf.gz create mode 100644 test/files/gzip.in create mode 100644 test/files/many.sdf.gz create mode 100644 test/files/tencmpds.sdf.gz create mode 100644 test/gziptest.cpp create mode 100644 test/multicmltest.cpp diff --git a/test/files/1ubq.pdb.gz b/test/files/1ubq.pdb.gz new file mode 100644 index 0000000000000000000000000000000000000000..d52faf5f8f9bf71ae38fdc7b58a28a62049afb23 GIT binary patch literal 18118 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/files/c3.cml.gz b/test/files/c3.cml.gz new file mode 100644 index 0000000000000000000000000000000000000000..e4473a77f60010f80daa8a76f07e8ac461f1c5de GIT binary patch literal 1678 zcmV;9266cxiwFpMlYCVG17kBTV{L2zwVGQ`V@C|f-}h4-<(ioSuUTRo*aL3#Q$-3_kS-RF21jC&KD-BJTdAzu~ z3^nKR`(btdcz5&b{r&m#`03^2WOELSVST;6S>HbX@%&?by!f^le&X>WI|u>1UkuOu z2_01&KP`KAl-h z)c&DY-)jTO6{6V4CqNSYd-X^X`u7qPBXETJ40;WoN=xuS2zw2lZ8bz8>{9rusD1xlgIj+L06Tv|6qf+@8a&&i z5K}C>4b0~t1`vA*ZvAsGNdS8do^3MdsTSYtK_m~L*WlK_LJW;RMR5(s`w*go!XJ`8A&%4{p=7b+V4x`2I>%SgwTxi3z z-QL|_uJ3thn?Xo+WU<3x+J0Z{P_?6B+9lt#3&VJvz1c0BRh!wkxxZ-_hv^hE*#)Cr zGT5bA^bXru`W@}E!7dx;n`iOUvsPy99k#Q~w;EMC+F9f>+FA4hJ2Ek)Zw^xZ0e>=;@D2?f6H^(Hc-N zi9t67Qv)g{(X7JKBB*1MEdv@RbrAr40t%{k5jfC*%IdQuqMk%WgxwnEDnEMpZBmh8 zw}@FrC^xW&O4|`soIUMnQ`keL2G{2@BIRjH#r4uhtDI2c)J^L#4!ULCsF0 z`YWi!LWcIjD`1vUL&tSyl?Hby%Q;j~sb5GnDdSgAX}*xbWmNnXR4UGcWh2l~sozMn z@oS_McoCTTS71GMCy6$HQr|Ws#rC>`s!Vz+s^>Z*Jw<<{=x?rHUH_y%ZRXs(&WBn> z{gR@;;$krEA*8=-Vwr7&DE~f()C(hDb_zHO%>E$%!bW{xZ$rp?h^E1yKd}2|1mkma z*-qgDxNa(@he=Wvytb6wY(^F;AS1Yt5yIX<#3r(ec5*gxRhspyqhGd#Ioob0vJvKr z@s6b!?^vo4?W>U?s}&)=^+o@GVRq9s#l-BU$)NS2maIHXRvspahd;~grdz?u(E9Xi zTB0!jA;5tYmf2==*pN)ODRp)|c%Vf#B#S7Jg-fPuQ|lelXh^25eH4X7J)>c;sAn?V Y6NP^5vRYo;T%UaT52|ot(^oH1CD%59g*UZEdTtsAHV+cuea|%eSLfV{OPyX z&o2~`%FB)7{NK(0UcP_*`1<U7spMUxC`PY}1pVzBC{&@TPa$7!hd%JBPohV0N zzID04&#T5;tHxMK85; zO!=$i=Wtm4AQx_K%mf;r;LI)$f0r zoxz^KF7RtunwZ_guZz8s8n$1F!LIOY0-OlHM!0X42pgr2~{$lnz*ut8_3sl-?>1>?$akRUG*3JEL?6 z8$Lfshg#qtr2|NuMmo^e9;L(5rj-t6M=2n1M{JMM0j0o$bl^wn5TQLv2b4Go>>#ZO z#SQSlZnHB;D<-g49`>b4slyy@?5|0aPp`N8r>yCs-J7Q(D{Aqu8S9Vrf<7$E=PpgK z%<{x)mD*3lt}V0oU@Ks}fwB0tGPV^+U6?@ym+6$eq-L`q%%g7aR#g+{`HxyL#FMd)pN`Em*T>#buElmSXs!%+D0} zz+?fFU9e`)io)cxtKHPzGur@b!R8IH4XoL-BAp~G&oqi>58ZqpOdBwnVpw+fo_qG7 zO8_i3U}f&hOI$to>oIY=EDH&OV)M(atHP_G){oE!mxx!sG;!`>>xB+VaW8? z!1xJgG{9N6XQfkdr*fv(Ict_iT(le|Jp?=IX0tTnBB7IQ0S|#`x6c>yvWfX&F4&hj zi~V+HR=$nKS(5?m=dHQ%&}cG%a*?bqjDm6h@Un6?ZvkKKCbtI_($0VOw+ONX2tb+fGjn{wO%%Z1ATT<6SlHvp>)ecH_j=I@wj}?8E!?Hde18cX(O5Kc>=v3;| z?vuNtZsv|Nji%3BsRn-U`PcziaGhsmFx3X2EQl`-Kxnte=4sn%zz`UBqv_buA=tqH z1nIzvEFK5*U1)GxAjZI&3@B?Sm%_Mb{RVMP>xszdny2b#1|W8cF7}jVUWny8(8k+EJqy$(_JZ} zJtcHB0R2P4aDSXiZui8?5m;LT=1meEo@rz3z#Qjsu++s_Ss@NjL9xkzvP9IB^C(zy zeVipfoam{o0i`-68V7415|$$D?Rhp$;qpwIq>Hm`YI`{saCN3lzK^pUhNqy^WB|EI zv#n5uz}gy6W?CPIXN?rQ(UjoE!P*Sa`G|c!?B)^JLk2Jn&$PK+2b6#a7G34r<@qf4 zC-^kn&tqPm&vKIiG(TKOOK6NtL0P&ui%P@uS@vC=G3juk=XM=XW?B*%2J6=WKC0Tc zqR%d_19I%ZTr{R+K%eE*jiz!OPW0Sn0MCo*Q803@8-QfzkVg`I7O>rDCLWzN$Z-dj zeHsmo`CKl5$$SnujDz(DMWh}N@nz|M@@&Tj_Ha-{rN&v#Cy)JXVA_FcUN_^&>$OBD z*JOZ{DTvqov$)^QBhFF>7X2!zQrzw45m@d%5$3u(V!#FaW+V*(1nxUtFUk}$T ze*=tfJzQgU=x;q}t+wYKS!_w&y1(k(a*fu1Mpej%rrajCh#H~9&Qp~EC6pJ zUl-6x$M&)@ z!@!$|Q}zlxTg+EGw%4skcnHhGF);<6Juslav&Ez_%+TIq&+H03TkKUk@N6+=%rM|v zjF~+G&sFuB9eB1_vK}$sS2ueEo-Jmo9eB3bFoqfZ%$q#|Zwez4foF>WV@AM#1;!%q zW@BCLz=MscF(cr=jj7oq^w-9&+QEMtJ7Y$GHyb0fC-gIe9opMi7&8LA*;tr8fuF{C zwPSnPm=WO9xHWr1djLD|Y#df0g7&!C6Yv~?fdo7oSH`daZ^nt)6YyLmw%M^gZ_EhX z*ZOYu3_M#Os~vc@J{mIuep+A5o`GlUOSJ>f=Cd&)=zreq8F;RT{LK!0ns3%44}O{t zX3x;y3=CxOKSPf^_;2?zdj>xZv)X}Y!?YfG;M1;M>{aVldj|XF|Eh=WuYjHIc5)1M t20ONwJ2UkqxN7%c4`2^qSFr2d6LJyk5$p-<3G9`h{{u4i)nxWW006X!`>X%} literal 0 HcmV?d00001 diff --git a/test/files/c5.smi.gz b/test/files/c5.smi.gz new file mode 100644 index 0000000000000000000000000000000000000000..c7a77d4a4ee85bf1186648c54c707cdfeee460cb GIT binary patch literal 154 zcmV;L0A>FliwFqUnS50M17kHVb8Tq=WsR{8!!QU$=lV-PS&E%F>CowxPImVw)2&hd ze++o5YGK6j4(LK7GYe<#*K`Qb765|km8+^ifJ&|uvMN38*XQ8odJQcdo;P&9jMV<*{pU}Ks!*nI**^UK;oEmV{p{f- zDMwCJLzHKi%O<=@+%`J>skLtZ4McR=?4Rx_1RLH`-KC6Ax^q?9o^JO{JpP$hDCKE| zvi47LuUtSYnRQx8HT6%j=ZabhQm2(rq<;!~6oIUyJ*445*^`E8C0OLQ4-sE#@#vOm zg?DLIp1;18vQsJTca?V4I<08)4_J|{gX^?Xldq`62>Mc_$3@v96uTALV>AY7(tuVPSqIp_TQYh<^ z<=XJ2tWSBD7H43m?Hm=FRw&T4l7r>$f3UdtI!dlHb7H{uNGx}GTH#!l6)fw*p2^q2 zD|14@?mEl+>5FAa$jge^d-@Vs5zQOUvV{EZWH{@Dcj&%GTre@{HC?TcHxw(a)C?cBiKv)j3RIyY?L=z|S4A8swg9{Ah2 zsef@Cm1C9!ZC@Pb=Jv&5u8p4fTiO?g(Ynbo`)o)KyZOPkz4!dt<;k|a{mqMSUwrf8 z=Z7!e`SMHq_`^?6E^n0vG;_K9dcsE^eEjUmh3z8EQRjExy?prm|1UrN-}d9fi?6=^ z;mIY2VAZf*F8{KBmroisunR$`@++u;xhRkQd;IfjYLIt| zlBSiY*u_=$9Q$=;WuM|GGr>w83SKFBdCN4c7{SOW*(r%VE>+!1Ldzu=kFMrn&nQzw z%VidxDP;5xZlZ8qkzMEJC0RVz4thtx#H^p0IP2$C;TlRHl{G2rlN!JV%!I zgYGwK&~_iB1|Hoc4(baiRhgAC;-Z7J4T1p+UK;$Klp&_rilk!EH7m*M2ZcJV^c?y( zv`69uql)j5C~^Y$6nyg;#BCFgu(b`H!0g!3^XtL)bOsqGsK1>%;QJBOpMd1%z!45l z26BY&+YxjcqUR$(wg1%#IUCW=Gw#~vG`5+E|NIjf$udLEW8Kb?YQkB^AWzk zO4)yX_&y8r01Q*HvZD=_dfkOq@z7E9<4{*H$XvZNCvs)KD;k(D3owv42?$^ru~Hws z)Vq3NRs;ieMvl~hR7y^3Y^Oj%!VA8~=On$c zRAOjmO~GE|s1r44MKQUR5`T$Od~uj+=?sFpO6!G;9WtY@FLBYcMyAWOLalH^ zR3gyzoy~pWf@PhZM)J3$RB=mhGCZ{7*oZg^BLY>z4bL6nQf-f6+?Q|Ye$<*Fa0q3f{&&t%a_l(8GKk&8EAwYHoVaiF=UHPq6Y;2oUS)X}2Uy6P3y-qnGR_n;f>s2Bvwqnm$17N=M!U?>g~IwA4S z)!yrmb=C9gjTSH00uqu2nH_lhJ5;0WXa_D``?Qm*stJ7YX{FhrT+u}(B9?U)gQYCf z@+DV{N;JLEyXxm@Y}z~2f!jNigTt{>yj=zVOBp8DyRYw1sDdBuH#{y$ubu2zzKFqX zCjlj0?mxQ-m6m z-e~XJU#-3WMg8!92krd~W}kP->ph+RM0+3o@Q6+i!gmgQukqc8cuKE_-hTn&tgSf| GI{*M@W59R- literal 0 HcmV?d00001 diff --git a/test/files/gzip.in b/test/files/gzip.in new file mode 100644 index 0000000000..ccc7c1c6d1 --- /dev/null +++ b/test/files/gzip.in @@ -0,0 +1,37 @@ +# c3.cml.gz +CC(Cn1cnc2c1c(=O)n(C)c(=O)n2C)O +CN(CCc1c[nH]c2c1c(O)ccc2)C +COc1c2oc(=O)ccc2c(c2c1occ2)CC(C(O)(C)C)O +#1ubq.pdb.gz +NCCCC[C@@H](C(=O)N[C@H](C(=O)NCC(=O)N[C@H](C(=O)N1CCC[C@H]1C(=O)N1CCC[C@H]1C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)NCC(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)NCC(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)N[C@H](C(=O)NCC(=O)NCC(=O)O)CCCN=C(N)N)CC(C)C)CCCN=C(N)N)CC(C)C)C(C)C)CC(C)C)CC1=NC=NC1)CC(C)C)[C@H](O)C)CO)CCC(=O)O)CCCCN)CCC(=O)N)[C@H](CC)C)CC(=O)N)Cc1ccc(cc1)O)CC(=O)O)CO)CC(C)C)[C@H](O)C)CCCN=C(N)N)CC(=O)O)CCC(=O)O)CC(C)C)CCC(=O)N)CCCCN)C)Cc1ccccc1)[C@H](CC)C)CC(C)C)CCCN=C(N)N)CCC(=O)N)CCC(=O)N)CC(=O)O)[C@H](CC)C)CCC(=O)O)NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@H]([C@H](CC)C)NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@H](C(C)C)NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@H]([C@H](CC)C)NC(=O)[C@H]([C@H](O)C)NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@@H]1CCCN1C(=O)[C@@H](NC(=O)[C@H](C(C)C)NC(=O)[C@@H](NC(=O)[C@@H](NC(=O)[C@H]([C@H](O)C)NC(=O)[C@H]([C@H](CC)C)NC(=O)[C@H]([C@H](O)C)NC(=O)[C@@H](NC(=O)CNC(=O)[C@H]([C@H](O)C)NC(=O)[C@@H](NC(=O)[C@H]([C@H](O)C)NC(=O)[C@@H](NC(=O)[C@H](C(C)C)NC(=O)[C@@H](NC(=O)[C@H]([C@H](CC)C)NC(=O)[C@@H](NC(=O)[C@H](CCSC)N)CCC(=O)N)Cc1ccccc1)CCCCN)CC(C)C)CCCCN)CC(C)C)CCC(=O)O)CCC(=O)O)CO)CC(=O)O)CCC(=O)O)CC(=O)N)CCCCN)C)CCCCN)CCC(=O)N)CC(=O)O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O +# tencmpds.sdf.gz +[O-]C(=O)CC(C[N+](C)(C)C)OC(=O)C +OC(=O)CC(C[N+](C)(C)C)OC(=O)C +OC1C=CC=C(C1O)C(=O)O +CC(CN)O +NCC(=O)COP(=O)(O)O +[O-][N+](=O)c1ccc(c(c1)[N+](=O)[O-])Cl +CCn1cnc2c1ncnc2N +CCC(C(C(=O)O)O)(O)C +OC1C(OP(=O)(O)O)C(O)C(C(C1O)O)O +O=CN(c1ccc(cc1)C(=O)NC(C(=O)O)CCC(=O)O)CC1CNc2c(N1)c(=O)nc([nH]2)N +#c4.mol2.gz +O=C/C=C/c1ccc(o1)[N](=O)O +COC(=O)/C=C/c1ccc(o1)[N](=O)O +C[NH+](Cc1ccccc1)C.Cl +CCCCCCCC[C@H]1O[C@@H]1CCCCCCCC(=O)O.CCCCCCCCCC[C@H]1O[C@@H]1CCCCCC(=O)O.[Mg] +#c5.smi.gz +COc1ccc2c(c1)c1c([nH]2)ccc2c1c[n+](CCN1CCC(CC1)C1CCN(CC1)CC[n+]1ccc3c(c1)c1c(cc3)[nH]c3c1cc(OC)cc3)cc2 +O=C(N(C)C)Nc1ccc(c(c1)Cl)Cl +CCCC(C(=O)O)CCC +NC(C(=O)O)CCCCCP(=O)(O)O +NC(C(=O)O)CCCCCCP(=O)(O)O +#many.sdf.gz contains concatenated gzip files +[O-][N+](=O)[O-].[O-][N+](=O)[O-].[Co+2] +ClCCCCCC(=O)OCC +#five_obabel.sdf.gz is the output of older babel versions +Clc1cccc(c1)Cn1c(C)nc2c(c1=O)cc(cc2)NC(=O)c1cccc(c1)Cl +O=C(C1CCCCC1)Nc1ccc2c(c1)c(=O)n(c(n2)C)Cc1ccc(cc1)F +Fc1ccc(cc1)Cn1c(C)nc2c(c1=O)cc(cc2)NC(=O)c1ccccc1 +O=C(C1CCCCC1)Nc1ccc2c(c1)c(=O)n(c(n2)C)Cc1ccc(cc1)Cl +Clc1ccc(cc1)Cn1c(C)nc2c(c1=O)cc(cc2)NC(=O)c1ccccc1 diff --git a/test/files/many.sdf.gz b/test/files/many.sdf.gz new file mode 100644 index 0000000000000000000000000000000000000000..7038d0872de5de07758efb927a9504d040e0f618 GIT binary patch literal 44743 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%0DMpYw0Z4om!C)#8D!`AoVpx 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ʇxKTjri#>COQSk*}d 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 acCNe|!={g8od#0x#{37aDq>LUjQ{|_t>!TR literal 0 HcmV?d00001 diff --git a/test/gziptest.cpp b/test/gziptest.cpp new file mode 100644 index 0000000000..61cea1dd6c --- /dev/null +++ b/test/gziptest.cpp @@ -0,0 +1,157 @@ +/********************************************************************** +gziptest.cpp - Test handling of gzipped files. + +Copyright (C) 2015 David R. Koes + +This file is part of the Open Babel project. +For more information, see + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +***********************************************************************/ + +// used to set import/export for Cygwin DLLs +#ifdef WIN32 +#define USING_OBDLL +#endif + +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace OpenBabel; + +//reads molecule(s) from file and compares to results +//outputs error and terminates if not consistent +void checkResults(const string& file, const vector& correctResults) +{ + if(correctResults.size() == 0) + return; + OBMol mol; + OBConversion conv(file); + conv.SetOutFormat(conv.GetInFormat(), conv.GetInGzipped()); + vector results; + while(conv.Read(&mol)) + { + OBConversion strconv; + OBMol mol2; + string gzippedstr = conv.WriteString(&mol); + strconv.SetInAndOutFormats(conv.GetInFormat(), conv.FindFormat("CAN"), conv.GetInGzipped()); + strconv.SetOptions("n",OBConversion::OUTOPTIONS); + strconv.ReadString(&mol2, gzippedstr); + string cansmi = strconv.WriteString(&mol2, true); + results.push_back(cansmi); + } + + if(results.size() != correctResults.size()) + { + cout << "Incorrect number of outputs (" << results.size() << " vs " << correctResults.size() << ") for " << file << "\n"; + exit(-1); + } + for(unsigned i = 0, n = results.size(); i < n; i ++) + { + if(results[i] != correctResults[i]) + { + cout << file << " match failure: " << results[i] << " != " << correctResults[i] << "\n"; + exit(-1); + } + } +} + +// 1 +// reads gzip.infiles, which has a file name on each line, +// converts each molecule into a gzipped string, converts that +// string into a molecule, converts that molecule into canonical smiles +// and compares to gzip.out + +int gziptest(int argc, char* argv[]) +{ + + int choice = 1; + + if (argc > 1) { + if(sscanf(argv[1], "%d", &choice) != 1) { + printf("Couldn't parse that input as a number\n"); + return -1; + } + } + + if(choice != 1) //eh, not bothering to split this up + { + cout << "Test number " << choice << " does not exist!\n"; + return -1; + } + + cout << endl << "# Testing gzip handling... " << endl; + + #ifdef TESTDATADIR + string testdatadir(TESTDATADIR); + string gzipin = testdatadir + "gzip.in"; + #else + string gzipin = "files/gzip.in"; + #endif + + #ifdef FORMATDIR + char env[BUFF_SIZE]; + snprintf(env, BUFF_SIZE, "BABEL_LIBDIR=%s", FORMATDIR); + putenv(env); + #endif + + ifstream ifs(gzipin.c_str()); + if (!ifs) + { + cout << "Bail out! Cannot read gzip.in!" << endl; + return(-1); + } + + //gzip.in specifies what files to read and their canonical smiles + string line; + string filepath; + vector correctResults; //read from file + + while(getline(ifs, line)) + { + if(line.length() == 0) + continue; //ignore blank lines + if(line[0] == '#') + { + checkResults(filepath, correctResults); + correctResults.clear(); + //next token should be file name + stringstream str(line); + string fname; + str >> fname; //# character + if(fname.length() == 1) + { + str >> fname; // actual file name + } + else + { + fname = fname.substr(1); //strip # + } +#ifdef TESTDATADIR + filepath = testdatadir + fname; + #else + filepath = "files/" + fname; +#endif + } + else + { + correctResults.push_back(line); + } + } + checkResults(filepath, correctResults); + + return(0); +} diff --git a/test/multicmltest.cpp b/test/multicmltest.cpp new file mode 100644 index 0000000000..a536fa4bfe --- /dev/null +++ b/test/multicmltest.cpp @@ -0,0 +1,95 @@ +/********************************************************************** +multicml.cpp - Test handling of cml files with multiple molecules. + +Copyright (C) 2015 David R. Koes + +This file is part of the Open Babel project. +For more information, see + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +***********************************************************************/ + +// used to set import/export for Cygwin DLLs +#ifdef WIN32 +#define USING_OBDLL +#endif + +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace OpenBabel; + +//The XML reader was reading up to . +//When Read was called on this, it would not return false. + +// 1 +// reads in molecules from a file +int multicmltest(int argc, char* argv[]) +{ + + int choice = 1; + + if (argc > 1) { + if(sscanf(argv[1], "%d", &choice) != 1) { + printf("Couldn't parse that input as a number\n"); + return -1; + } + } + + if(choice != 1) //eh, not bothering to split this up + { + cout << "Test number " << choice << " does not exist!\n"; + return -1; + } + + cout << endl << "# Testing handling of multi-molecule cml files... " << endl; + + #ifdef TESTDATADIR + string testdatadir(TESTDATADIR); + string infile = testdatadir + "c3.cml"; + #else + string infile = "files/c3.cml"; + #endif + + #ifdef FORMATDIR + char env[BUFF_SIZE]; + snprintf(env, BUFF_SIZE, "BABEL_LIBDIR=%s", FORMATDIR); + putenv(env); + #endif + + ifstream ifs(infile.c_str()); + if (!ifs) + { + cout << "Bail out! Cannot read c3.cml!" << endl; + return(-1); + } + + OBMol mol; + OBConversion conv(&ifs); + conv.SetInFormat("CML"); + int cnt = 0; + while(conv.Read(&mol)) + { + cnt++; + } + + if(cnt != 3) + { + cout << "Wrong number of molecules found in file " << cnt << "\n"; + return -1; + } + return(0); +} From 4b16912df8e591f5deadd76b3af75bfd11453547 Mon Sep 17 00:00:00 2001 From: dkoes Date: Sun, 14 Jun 2015 15:47:51 -0400 Subject: [PATCH 13/20] fix for multifile zip support --- src/zipstream.h | 1 + src/zipstreamimpl.h | 11 +++++++---- test/files/gzip.in | 6 +++--- test/files/many.sdf.gz | Bin 44743 -> 1621 bytes 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/zipstream.h b/src/zipstream.h index 9032716577..3e490bdc65 100644 --- a/src/zipstream.h +++ b/src/zipstream.h @@ -210,6 +210,7 @@ class basic_unzip_streambuf : byte_vector_type _input_buffer; char_vector_type _buffer; unsigned long _crc; + unsigned long _unzipped_component_bytes; //keep track of bytes that were in separately zipped sections for seeking purposes }; //***************************************************************************** diff --git a/src/zipstreamimpl.h b/src/zipstreamimpl.h index 865729e17f..fc2cea5b25 100644 --- a/src/zipstreamimpl.h +++ b/src/zipstreamimpl.h @@ -308,7 +308,8 @@ basic_unzip_streambuf::basic_unzip_streambuf(istream_reference is _istream(istream), _input_buffer(input_buffer_size), _buffer(read_buffer_size), - _crc(0) + _crc(0), + _unzipped_component_bytes(0) { initialize(window_size); } @@ -382,7 +383,7 @@ template std::streampos basic_unzip_streambuf::currentpos() { - return _zip_stream.total_out - std::streamoff(this->egptr() - this->gptr()); + return _unzipped_component_bytes + _zip_stream.total_out - std::streamoff(this->egptr() - this->gptr()); } template @@ -427,6 +428,7 @@ std::streampos _istream.seekg(0); this->initialize(-15); this->check_header(); + _unzipped_component_bytes = 0; } // Now we keep going, throwing away the data until we get to the right place @@ -446,6 +448,7 @@ std::streampos _istream.clear(std::ios::goodbit); _istream.seekg(0); + _unzipped_component_bytes = 0; this->initialize(-15); this->check_header(); @@ -570,9 +573,9 @@ basic_unzip_streambuf::unzip_from_stream(char_type* buffer, if (_err == Z_STREAM_END) { //dkoes, support concatenated zip files put_back_from_zip_stream(); - // we do _not_ reset the zipstream since seeking requires we keep a running total + _unzipped_component_bytes += _zip_stream.total_out; //needed for seeking + inflateReset(&_zip_stream); //read footer - for(unsigned i = 0; i < 8; i++) { get_istream().get(); //but ignore since for some reason check_footer is in the stream class.. and isn't called anyway diff --git a/test/files/gzip.in b/test/files/gzip.in index ccc7c1c6d1..2366be8f73 100644 --- a/test/files/gzip.in +++ b/test/files/gzip.in @@ -1,3 +1,6 @@ +#many.sdf.gz contains concatenated gzip files +[O-][N+](=O)[O-].[O-][N+](=O)[O-].[Co+2] +ClCCCCCC(=O)OCC # c3.cml.gz CC(Cn1cnc2c1c(=O)n(C)c(=O)n2C)O CN(CCc1c[nH]c2c1c(O)ccc2)C @@ -26,9 +29,6 @@ O=C(N(C)C)Nc1ccc(c(c1)Cl)Cl CCCC(C(=O)O)CCC NC(C(=O)O)CCCCCP(=O)(O)O NC(C(=O)O)CCCCCCP(=O)(O)O -#many.sdf.gz contains concatenated gzip files -[O-][N+](=O)[O-].[O-][N+](=O)[O-].[Co+2] -ClCCCCCC(=O)OCC #five_obabel.sdf.gz is the output of older babel versions Clc1cccc(c1)Cn1c(C)nc2c(c1=O)cc(cc2)NC(=O)c1cccc(c1)Cl O=C(C1CCCCC1)Nc1ccc2c(c1)c(=O)n(c(n2)C)Cc1ccc(cc1)F diff --git a/test/files/many.sdf.gz b/test/files/many.sdf.gz index 7038d0872de5de07758efb927a9504d040e0f618..33c71138122d0ca85235d90973bbec36633102bd 100644 GIT binary patch literal 1621 zcmV-b2CDfViwFqQrhHWZ12HahWM%-Rlu>V+Fc8Pz`4l|tAz1_k18Gnfq{BGH8rT$@ zwzR5H*Hvnztx>n``_B-X6+)Iov7Ff4{qM)woQ*uovNT;cIKfYkKMf1nr?zLgo`)h$ z*?nD~>bj-Z&FXdE_w`G(idT=Onl(aXB5yToZFt!PpiRWb-cVchx9z*$;i??3eF}WN=aZG1A{TRDJ+0lCxXUo1EMzQ<3uocBQh~oBh*_)y&YNoR2 zS)DJ3__4FUD6r;WC2O?4f|=Z&wX)ZY4M)N`jD-FEcaan@@6daMbAMOvVL z$%+uiB&{${GhVQ$!ZZpr)SS>^KQPWHhJ@Y_rL?;$!17Ww;)GmNSt`NIoDFH>kSiKs zOft#yK$~tCMV!?NYe4l7nGGs;(FBS{nr2vC<;>fkCnJNV#>e@x7Z_Q>-7oGBi;39#%TJSto zlR(2uhWK=g>Wa|wShhG9`M%c?zJFwOvMQ0%gs3;)vdP_O&c~x$ZrqLQKXb^0mlJe1 z2%~}@4{MK)%!FE^jr!|jT)jo|Ow~eXiJLnn)hSIX97BFi`e!sIa9LI$d40(gOhNpP zzwtz5QK`bq34a$^o1$WuYIU3JBie~tul!@;16L*^OuoCF!Oew2uHVtSO*zja%0QAz zmKmwtp1PV<^?XuI`wM88^b1D`001A02ngP$d{qDgGA?suW&o{M(QczS6n*C_{ICx* z(n4(mHb~n@j&XuDfN3C|CNHSUM$JgsR+_D}Gk-r9(nh5?+q8^@66^Tf<9ko8Ekc7h zjva(tqvUS;kBcecB*Z}&(!_BPWr&6dVbl)%>VZgfO)hY3*(5u#2ur;JrXl&%|)KC!aU!{#O}-aDv^g7J>l;8 zDxnv!fQ+pu@?si|_T~5+jjD)x5o36~N*6J*A`TS=yqyt`LNYH@()aWWk2xEw5_+*u z*x>|F8pI#Yg+_d=3ilZEN9Phj@8Z$9gwQYaH@J&gI9et4e9A`mOXP(#9<4&bT^^2g z*Y_w3M*q^(^U2{1LPZh#pbCMCd~%p!);rVhQep9PEG0hqhzbNn-DeMl3pBQ&1P@Wa zfUI76IHcC1*ASR?#Wo;I)<7rZ2ik5+GjD@L1EvA`j zTI$5%Hd3{mR$tVrnm9s;WV4z6mkXi9nPYyZyKgDX-JQ}>!h9+OfbvTAm>_xAF6wDV zKkhNt)tkFqUiLEA%Tmb3l&VlG)f6IE6KDIl`?*KV-R$-c-ye3{AGh}pw~t#(oD|hl zOrQO2gKjt9{zeC-?Z1!P@0RHI?pCd;TIHhF*3jGgpZoudt`{QFwKj#wM&zsund*t7 z@8wQklIlW6DGBA?(2EUmsY{3(eIocXa|73RLzlbp#g4i>rGJvtKcxpfGjXm~>sgtt zuk!V5D%S2Yoy}&#tSaFay||mq%uBx!0(#l&p9*yYec0rpf(|4c-mN*qVZT#zlPS5( z#G;v*MGljRb1dNm@vGAFv_2@p>p|F~J$DYA)~3pJreJF+jr??JKFeGfIRU>NC-apHt73n+MEhTE! zKnE_Ad1m1uY55lGaMhecdQ<9H`peAm@H#D2Q6CGl+AEf|Il(Yyk?2Cys;O6{YL`QY ThR_9nz;J#6VIi$lX%7GZ&9f%U literal 44743 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%0DMpYw0Z4om!C)#8D!`AoVpx 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ʇxKTjri#>COQSk*}d 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 Date: Sun, 14 Jun 2015 16:08:41 -0400 Subject: [PATCH 14/20] modified 1ubq --- test/files/1ubq.pdb.gz | Bin 18118 -> 13507 bytes test/files/gzip.in | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/files/1ubq.pdb.gz b/test/files/1ubq.pdb.gz index d52faf5f8f9bf71ae38fdc7b58a28a62049afb23..344cecea245468770ea790c40260ee10912410c0 100644 GIT binary patch literal 13507 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 literal 18118 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 Date: Sun, 14 Jun 2015 21:34:22 -0400 Subject: [PATCH 15/20] forgot to remove boost includes --- src/obconversion.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index ff81cfa0de..da0bb97817 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -56,12 +56,6 @@ GNU General Public License for more details. extern "C" int strncasecmp(const char *s1, const char *s2, size_t n); #endif -#include -#include -#include -#include -#include - #ifndef BUFF_SIZE #define BUFF_SIZE 32768 #endif From 60c1e3b383bb915555b29dcd2a61cfb154b093e0 Mon Sep 17 00:00:00 2001 From: David Koes Date: Tue, 16 Jun 2015 07:58:11 -0400 Subject: [PATCH 16/20] wow, serious errors that the compiler didn't catch --- src/obconversion.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index da0bb97817..73bbc4974c 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -302,6 +302,8 @@ namespace OpenBabel { m_IsFirstInput = o.m_IsFirstInput; SkippedMolecules = o.SkippedMolecules; pAuxConv = o.pAuxConv; + + return *this; } /////////////////////////////////////////////// @@ -799,7 +801,7 @@ namespace OpenBabel { OBFormat* OBConversion::FormatFromExt(const char* filename) { bool isgzip; - FormatFromExt(filename, isgzip); + return FormatFromExt(filename, isgzip); } OBFormat* OBConversion::FormatFromExt(const std::string filename) From f643c92945182cfa60e9a69ae49f0ad86671f6e1 Mon Sep 17 00:00:00 2001 From: David Koes Date: Tue, 16 Jun 2015 08:07:08 -0400 Subject: [PATCH 17/20] Fix compiler warnings in obconversion --- src/obconversion.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index 73bbc4974c..717e67d971 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -232,7 +232,7 @@ namespace OpenBabel { EndNumber(0), Count(-1), m_IsFirstInput(true), m_IsLast(true), MoreFilesToCome(false), OneObjectOnly(false), SkippedMolecules(false), inFormatGzip(false), outFormatGzip(false), - pOb1(NULL), pAuxConv(NULL),wInpos(0),wInlen(0) + pOb1(NULL),wInpos(0),wInlen(0),pAuxConv(NULL) { SetInStream(is); SetOutStream(os); @@ -250,7 +250,7 @@ namespace OpenBabel { EndNumber(0), Count(-1), m_IsFirstInput(true), m_IsLast(true), MoreFilesToCome(false), OneObjectOnly(false), SkippedMolecules(false), inFormatGzip(false), outFormatGzip(false), - pOb1(NULL), pAuxConv(NULL),wInpos(0),wInlen(0) + pOb1(NULL), wInpos(0),wInlen(0), pAuxConv(NULL) { //These options take a parameter RegisterOptionParam("f", NULL, 1,GENOPTIONS); @@ -303,7 +303,7 @@ namespace OpenBabel { SkippedMolecules = o.SkippedMolecules; pAuxConv = o.pAuxConv; - return *this; + return *this; } /////////////////////////////////////////////// @@ -860,7 +860,7 @@ namespace OpenBabel { // catch last molecule acording to -l Count++; bool success = false; - if (EndNumber==0 || Count<=EndNumber) { + if (EndNumber==0 || (unsigned)Count<=EndNumber) { success = pInFormat->ReadMolecule(pOb, this); } From b82d8f49d934ae6c3e4808683a11b8dff667ea8f Mon Sep 17 00:00:00 2001 From: David Koes Date: Thu, 18 Jun 2015 17:18:50 -0400 Subject: [PATCH 18/20] improve backwards compat support for auto-detecting gzip instreams --- src/obconversion.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index 717e67d971..d93ada6312 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -461,6 +461,10 @@ namespace OpenBabel { StreamState savedIn, savedOut; if (is) { + if(!inFormatGzip && pInFormat && zlib_stream::isGZip(*is)) + { + inFormatGzip = true; + } savedIn.pushInput(*this); SetInStream(is, false); } @@ -824,7 +828,7 @@ namespace OpenBabel { { if(pin) { //for backwards compatibility, attempt to detect a gzip file - if(pInFormat && zlib_stream::isGZip(*pin)) + if(!inFormatGzip && pInFormat && zlib_stream::isGZip(*pin)) { inFormatGzip = true; } @@ -1052,6 +1056,12 @@ namespace OpenBabel { return false; } + if(!inFormatGzip && pInFormat && zlib_stream::isGZip(*pInput)) + { + //for backwards compat, attempt to autodetect gzip + inFormatGzip = true; + } + // save the filename InFilename = filePath; ios_base::openmode imode = ios_base::in|ios_base::binary; //now always binary because may be gzipped From 9065c661c798e5def031aec2cbe5fd9b5199a944 Mon Sep 17 00:00:00 2001 From: David Koes Date: Thu, 18 Jun 2015 17:35:12 -0400 Subject: [PATCH 19/20] put auto-gzip check for ReadFile in correct place --- src/obconversion.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/obconversion.cpp b/src/obconversion.cpp index d93ada6312..e7939c9e33 100644 --- a/src/obconversion.cpp +++ b/src/obconversion.cpp @@ -1056,12 +1056,6 @@ namespace OpenBabel { return false; } - if(!inFormatGzip && pInFormat && zlib_stream::isGZip(*pInput)) - { - //for backwards compat, attempt to autodetect gzip - inFormatGzip = true; - } - // save the filename InFilename = filePath; ios_base::openmode imode = ios_base::in|ios_base::binary; //now always binary because may be gzipped @@ -1073,6 +1067,12 @@ namespace OpenBabel { return false; } + if(!inFormatGzip && pInFormat && zlib_stream::isGZip(*ifs)) + { + //for backwards compat, attempt to autodetect gzip + inFormatGzip = true; + } + SetInStream(ifs, true); return Read(pOb); } From 8342a8146afa197458d1e93f7828369159c2acf6 Mon Sep 17 00:00:00 2001 From: David Koes Date: Fri, 19 Jun 2015 17:05:45 -0400 Subject: [PATCH 20/20] do the right thing when the output file ends in .gz the is new behavior taking advantage of the improved gzip support --- tools/obabel.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/obabel.cpp b/tools/obabel.cpp index 573c3f57f3..b490948685 100644 --- a/tools/obabel.cpp +++ b/tools/obabel.cpp @@ -60,6 +60,7 @@ int main(int argc,char *argv[]) OBFormat* pInFormat = NULL; OBFormat* pOutFormat = NULL; + bool outGzip = false; vector FileList, OutputFileList; string OutputFileName; @@ -318,7 +319,7 @@ int main(int argc,char *argv[]) if (!gotOutType) { //check there is a valid output format, but the extension will be re-interpreted in OBConversion - pOutFormat = Conv.FormatFromExt(OutputFileName.c_str()); + pOutFormat = Conv.FormatFromExt(OutputFileName.c_str(), outGzip); if(OutputFileName.empty() || pOutFormat==NULL) { cerr << "Missing or unknown output file or format spec or possibly a misplaced option.\n" @@ -327,12 +328,12 @@ int main(int argc,char *argv[]) } } - if(!Conv.SetInFormat(pInFormat)) + if(!Conv.SetInFormat(pInFormat)) //rely on autodetection for gzipped input { cerr << "Invalid input format" << endl; usage(); } - if(!Conv.SetOutFormat(pOutFormat)) + if(!Conv.SetOutFormat(pOutFormat, outGzip)) { cerr << "Invalid output format" << endl; usage();