/*************************************************************************** * Copyright (c) 2011 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #endif #include #include #include #include "Writer.h" #include "Base64.h" #include "Base64Filter.h" #include "Exception.h" #include "FileInfo.h" #include "Persistence.h" #include "Stream.h" #include "Tools.h" #include #include using namespace Base; // boost iostream filter to escape ']]>' in text file saved into CDATA section. // It does not check if the character is valid utf8 or not. struct cdata_filter { using char_type = char; using category = boost::iostreams::output_filter_tag; template inline bool put(Device& dev, char ch) { switch (state) { case 0: case 1: if (ch == ']') { ++state; } else { state = 0; } break; case 2: if (ch == '>') { static const char escape[] = "]]>(); auto* filteredStream = dynamic_cast(CharStream.get()); filteredStream->push(cdata_filter()); filteredStream->push(Stream()); *filteredStream << std::setprecision(std::numeric_limits::digits10 + 1); } return *CharStream; } std::ostream& Writer::endCharStream() { if (CharStream) { CharStream.reset(); if (charStreamFormat == CharStreamFormat::Raw) { Stream() << "]]>"; } } return Stream(); } std::ostream& Writer::charStream() { if (!CharStream) { throw Base::RuntimeError("Writer::endCharStream(): no current character stream"); } return *CharStream; } void Writer::insertText(const std::string& str) { beginCharStream() << str; endCharStream(); } void Writer::insertAsciiFile(const char* FileName) { Base::FileInfo fi(FileName); Base::ifstream from(fi); if (!from) { throw Base::FileException("Writer::insertAsciiFile() Could not open file!"); } Stream() << "" << std::endl; } void Writer::insertBinFile(const char* FileName) { Base::FileInfo fi(FileName); Base::ifstream from(fi, std::ios::in | std::ios::binary | std::ios::ate); if (!from) { throw Base::FileException("Writer::insertAsciiFile() Could not open file!"); } Stream() << " bytes(static_cast(fileSize)); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) from.read(reinterpret_cast(bytes.data()), fileSize); Stream() << Base::base64_encode(bytes.data(), static_cast(fileSize)); Stream() << "]]>" << std::endl; } void Writer::setForceXML(bool on) { forceXML = on; } bool Writer::isForceXML() const { return forceXML; } void Writer::setFileVersion(int version) { fileVersion = version; } int Writer::getFileVersion() const { return fileVersion; } void Writer::setMode(const std::string& mode) { Modes.insert(mode); } void Writer::setModes(const std::set& modes) { Modes = modes; } bool Writer::getMode(const std::string& mode) const { std::set::const_iterator it = Modes.find(mode); return (it != Modes.end()); } std::set Writer::getModes() const { return Modes; } void Writer::clearMode(const std::string& mode) { std::set::iterator it = Modes.find(mode); if (it != Modes.end()) { Modes.erase(it); } } void Writer::clearModes() { Modes.clear(); } void Writer::addError(const std::string& msg) { Errors.push_back(msg); } bool Writer::hasErrors() const { return (!Errors.empty()); } void Writer::clearErrors() { Errors.clear(); } std::vector Writer::getErrors() const { return Errors; } std::string Writer::addFile(const char* Name, const Base::Persistence* Object) { // always check isForceXML() before requesting a file! assert(!isForceXML()); FileEntry temp; temp.FileName = Name ? Name : ""; if (FileNameManager.containsName(temp.FileName)) { temp.FileName = FileNameManager.makeUniqueName(temp.FileName); } temp.Object = Object; FileList.push_back(temp); FileNameManager.addExactName(temp.FileName); // return the unique file name return temp.FileName; } void Writer::incInd() { if (indent < 1020) { indBuf[indent] = ' '; indBuf[indent + 1] = ' '; indBuf[indent + 2] = ' '; indBuf[indent + 3] = ' '; indBuf[indent + 4] = '\0'; indent += 4; } } void Writer::decInd() { if (indent >= 4) { indent -= 4; } else { indent = 0; } indBuf[indent] = '\0'; } void Writer::putNextEntry(const char* file, const char* obj) { ObjectName = obj ? obj : file; } // ---------------------------------------------------------------------------- ZipWriter::ZipWriter(const char* FileName) : ZipStream(FileName) { #ifdef _MSC_VER ZipStream.imbue(std::locale::empty()); #else ZipStream.imbue(std::locale::classic()); #endif ZipStream.precision(std::numeric_limits::digits10 + 1); ZipStream.setf(std::ios::fixed, std::ios::floatfield); } ZipWriter::ZipWriter(std::ostream& os) : ZipStream(os) { #ifdef _MSC_VER ZipStream.imbue(std::locale::empty()); #else ZipStream.imbue(std::locale::classic()); #endif ZipStream.precision(std::numeric_limits::digits10 + 1); ZipStream.setf(std::ios::fixed, std::ios::floatfield); } void ZipWriter::putNextEntry(const char* file, const char* obj) { Writer::putNextEntry(file, obj); ZipStream.putNextEntry(file); } void ZipWriter::writeFiles() { // use a while loop because it is possible that while // processing the files new ones can be added size_t index = 0; while (index < FileList.size()) { FileEntry entry = FileList[index]; putNextEntry(entry.FileName.c_str()); indent = 0; indBuf[0] = 0; entry.Object->SaveDocFile(*this); index++; } } ZipWriter::~ZipWriter() { ZipStream.close(); } // ---------------------------------------------------------------------------- FileWriter::FileWriter(const char* DirName) : DirName(DirName) {} FileWriter::~FileWriter() = default; void FileWriter::putNextEntry(const char* file, const char* obj) { Writer::putNextEntry(file, obj); std::string fileName = DirName + "/" + file; this->FileStream.open(fileName.c_str(), std::ios::out | std::ios::binary); } bool FileWriter::shouldWrite(const std::string& /*name*/, const Base::Persistence* /*obj*/) const { return true; } void FileWriter::writeFiles() { // use a while loop because it is possible that while // processing the files new ones can be added size_t index = 0; this->FileStream.close(); while (index < FileList.size()) { FileEntry entry = FileList[index]; if (shouldWrite(entry.FileName, entry.Object)) { std::string filePath = entry.FileName; std::string::size_type pos = 0; while ((pos = filePath.find('/', pos)) != std::string::npos) { std::string dirName = DirName + "/" + filePath.substr(0, pos); pos++; Base::FileInfo fi(dirName); fi.createDirectory(); } putNextEntry(entry.FileName.c_str()); indent = 0; indBuf[0] = 0; entry.Object->SaveDocFile(*this); this->FileStream.close(); } index++; } }