Something went wrong. Try again.
A fork of https://github.com/crosspoint-reader/crosspoint-reader
Something went wrong. Try again.
5.8 kB · 176 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177#include "OpdsParser.h"
#include <Logging.h>#include <XmlParserUtils.h>
#include <cstring>
OpdsParser::OpdsParser() { parser = XML_ParserCreate(nullptr); if (!parser) { errorOccured = true; LOG_DBG("OPDS", "Couldn't allocate memory for parser"); }}
OpdsParser::~OpdsParser() { destroyXmlParser(parser); }
size_t OpdsParser::write(uint8_t c) { return write(&c, 1); }
size_t OpdsParser::write(const uint8_t* xmlData, const size_t length) { if (errorOccured) return length;
XML_SetUserData(parser, this); XML_SetElementHandler(parser, startElement, endElement); XML_SetCharacterDataHandler(parser, characterData);
const char* currentPos = reinterpret_cast<const char*>(xmlData); size_t remaining = length; constexpr size_t chunkSize = 1024;
while (remaining > 0) { void* const buf = XML_GetBuffer(parser, chunkSize); if (!buf) { errorOccured = true; LOG_DBG("OPDS", "Couldn't allocate memory for buffer"); destroyXmlParser(parser); return length; }
const size_t toRead = remaining < chunkSize ? remaining : chunkSize; memcpy(buf, currentPos, toRead);
if (XML_ParseBuffer(parser, static_cast<int>(toRead), 0) == XML_STATUS_ERROR) { errorOccured = true; LOG_DBG("OPDS", "Parse error at line %lu: %s", XML_GetCurrentLineNumber(parser), XML_ErrorString(XML_GetErrorCode(parser))); destroyXmlParser(parser); return length; } currentPos += toRead; remaining -= toRead; } return length;}
void OpdsParser::flush() { if (XML_Parse(parser, nullptr, 0, XML_TRUE) != XML_STATUS_OK) { errorOccured = true; destroyXmlParser(parser); }}
bool OpdsParser::error() const { return errorOccured; }
void OpdsParser::clear() { entries.clear(); searchTemplate.clear(); nextPageUrl.clear(); prevPageUrl.clear(); currentEntry = OpdsEntry{}; currentText.clear(); inEntry = inTitle = inAuthor = inAuthorName = inId = false;}
std::vector<OpdsEntry> OpdsParser::getBooks() const { std::vector<OpdsEntry> books; for (const auto& entry : entries) { if (entry.type == OpdsEntryType::BOOK) books.push_back(entry); } return books;}
const char* OpdsParser::findAttribute(const XML_Char** atts, const char* name) { for (int i = 0; atts[i]; i += 2) { if (strcmp(atts[i], name) == 0) return atts[i + 1]; } return nullptr;}
void XMLCALL OpdsParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) { auto* self = static_cast<OpdsParser*>(userData);
if (strcmp(name, "link") == 0 || strstr(name, ":link") != nullptr) { const char* href = findAttribute(atts, "href"); if (href) { const char* rel = findAttribute(atts, "rel"); const char* type = findAttribute(atts, "type");
if (rel && strcmp(rel, "search") == 0) { std::string sHref(href); if (sHref.find("{searchTerms}") != std::string::npos) { self->searchTemplate = sHref; } } else if (rel && strcmp(rel, "next") == 0 && !self->inEntry) { self->nextPageUrl = href; } else if (rel && strcmp(rel, "previous") == 0 && !self->inEntry) { self->prevPageUrl = href; }
if (self->inEntry) { if (rel && type && strstr(rel, "opds-spec.org/acquisition") != nullptr && strcmp(type, "application/epub+zip") == 0) { self->currentEntry.type = OpdsEntryType::BOOK; self->currentEntry.href = href; } else if (type && strstr(type, "application/atom+xml") != nullptr) { if (self->currentEntry.type != OpdsEntryType::BOOK) { self->currentEntry.type = OpdsEntryType::NAVIGATION; self->currentEntry.href = href; } } } } }
if (strcmp(name, "entry") == 0 || strstr(name, ":entry") != nullptr) { self->inEntry = true; self->currentEntry = OpdsEntry{}; return; }
if (!self->inEntry) return;
if (strcmp(name, "title") == 0 || strstr(name, ":title") != nullptr) { self->inTitle = true; self->currentText.clear(); } else if (strcmp(name, "author") == 0 || strstr(name, ":author") != nullptr) { self->inAuthor = true; } else if (self->inAuthor && (strcmp(name, "name") == 0 || strstr(name, ":name") != nullptr)) { self->inAuthorName = true; self->currentText.clear(); } else if (strcmp(name, "id") == 0 || strstr(name, ":id") != nullptr) { self->inId = true; self->currentText.clear(); }}
void XMLCALL OpdsParser::endElement(void* userData, const XML_Char* name) { auto* self = static_cast<OpdsParser*>(userData);
if (strcmp(name, "entry") == 0 || strstr(name, ":entry") != nullptr) { if (!self->currentEntry.title.empty() && !self->currentEntry.href.empty()) { self->entries.push_back(self->currentEntry); } self->inEntry = false; } else if (self->inEntry) { if (strcmp(name, "title") == 0 || strstr(name, ":title") != nullptr) { if (self->inTitle) self->currentEntry.title = self->currentText; self->inTitle = false; } else if (strcmp(name, "author") == 0 || strstr(name, ":author") != nullptr) { self->inAuthor = false; } else if (self->inAuthorName && (strcmp(name, "name") == 0 || strstr(name, ":name") != nullptr)) { self->currentEntry.author = self->currentText; self->inAuthorName = false; } else if (strcmp(name, "id") == 0 || strstr(name, ":id") != nullptr) { if (self->inId) self->currentEntry.id = self->currentText; self->inId = false; } }}
void XMLCALL OpdsParser::characterData(void* userData, const XML_Char* s, const int len) { auto* self = static_cast<OpdsParser*>(userData); if (self->inTitle || self->inAuthorName || self->inId) { self->currentText.append(s, len); }}