1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
/*
* Copyright (C) 2022 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSES/README.md for more information.
*/
#include "XBMCTinyXML2.h"
#include "RegExp.h"
#include "filesystem/File.h"
#include <cstdint>
#include <utility>
#include <vector>
namespace
{
static constexpr int MAX_ENTITY_LENGTH = 8; // size of largest entity "&#xNNNN;"
static constexpr size_t BUFFER_SIZE = 4096;
} // namespace
bool CXBMCTinyXML2::LoadFile(const std::string& filename)
{
XFILE::CFile file;
std::vector<uint8_t> buffer;
if (file.LoadFile(filename, buffer) <= 0)
{
// SetError private in tinyxml2 - replacement?
// tinyxml2::XMLDocument::SetError(tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED, NULL, NULL);
return false;
}
Parse(std::string_view(reinterpret_cast<char*>(buffer.data()), buffer.size()));
if (Error())
return false;
return true;
}
bool CXBMCTinyXML2::LoadFile(FILE* file)
{
std::string data;
char buf[BUFFER_SIZE] = {};
size_t result;
while ((result = fread(buf, 1, BUFFER_SIZE, file)) > 0)
data.append(buf, result);
return Parse(std::move(data));
}
bool CXBMCTinyXML2::SaveFile(const std::string& filename) const
{
XFILE::CFile file;
if (file.OpenForWrite(filename, true))
{
tinyxml2::XMLPrinter printer;
Accept(&printer);
const ssize_t sizeToWrite = printer.CStrSize() - 1; // strip trailing '\0'
bool suc = file.Write(printer.CStr(), sizeToWrite) == sizeToWrite;
if (suc)
file.Flush();
return suc;
}
return false;
}
bool CXBMCTinyXML2::Parse(std::string_view inputdata)
{
// TinyXML2 only uses UTF-8 charset
// Preprocess string, replacing '&' with '& for invalid XML entities
size_t pos = inputdata.find('&');
if (pos == std::string::npos)
{
return (tinyxml2::XML_SUCCESS ==
tinyxml2::XMLDocument::Parse(
inputdata.data(), inputdata.size())); // nothing to fix, process data directly
}
return ParseHelper(pos, std::string{inputdata});
}
bool CXBMCTinyXML2::Parse(std::string&& inputdata)
{
// TinyXML2 only uses UTF-8 charset
// Preprocess string, replacing '&' with '& for invalid XML entities
size_t pos = inputdata.find('&');
if (pos == std::string::npos)
{
return (tinyxml2::XML_SUCCESS ==
tinyxml2::XMLDocument::Parse(
inputdata.c_str(), inputdata.size())); // nothing to fix, process data directly
}
return ParseHelper(pos, std::move(inputdata));
}
bool CXBMCTinyXML2::ParseHelper(size_t pos, std::string&& inputdata)
{
CRegExp re(false, CRegExp::asciiOnly,
"^&(amp|lt|gt|quot|apos|#x[a-fA-F0-9]{1,4}|#[0-9]{1,5});.*");
do
{
if (re.RegFind(inputdata, static_cast<unsigned int>(pos), MAX_ENTITY_LENGTH) < 0)
inputdata.insert(pos + 1, "amp;");
pos = inputdata.find('&', pos + 1);
} while (pos != std::string::npos);
return (tinyxml2::XML_SUCCESS ==
tinyxml2::XMLDocument::Parse(inputdata.c_str(), inputdata.size()));
}
|