#include "pugixml.hpp" #include inline bool is_content_root(const char* name) { return std::strcmp(name, "abstract") == 0 || std::strcmp(name, "body") == 0; } // Sums length of all node_pcdata descendants (CDATA excluded to match // quick-xml's Event::Text-only accounting). size_t sum_pcdata(const pugi::xml_node& node) { size_t total = 0; for (pugi::xml_node child = node.first_child(); child; child = child.next_sibling()) { if (child.type() == pugi::node_pcdata) { total += std::strlen(child.value()); } else if (child.type() == pugi::node_element) { total += sum_pcdata(child); } } return total; } // Searches for / anywhere in the tree; once found, all // descendant text counts regardless of further nesting. size_t walk(const pugi::xml_node& node) { size_t total = 0; for (pugi::xml_node child = node.first_child(); child; child = child.next_sibling()) { if (child.type() != pugi::node_element) continue; if (is_content_root(child.name())) { total += sum_pcdata(child); } else { total += walk(child); } } return total; } size_t pubmed_len(char* buffer, size_t len) { pugi::xml_document doc; unsigned int flags = pugi::parse_default | pugi::parse_escapes; doc.load_buffer_inplace(buffer, len, flags); return walk(doc); } size_t pubmed_len_with_entities(char* buffer, size_t len) { pugi::xml_document doc; unsigned int flags = pugi::parse_default; doc.load_buffer_inplace(buffer, len, flags); return walk(doc); }