#include "paginate.h" #include "core/LoBBSReply.h" #include "platforms/LoPlatform.h" #include #include #include #include "core/LoBBSStackGuard.h" static constexpr size_t LOBBS_PAGER_FOOTER_MAX = 16; static constexpr int LOBBS_PAGER_MAX_PAGES = 64; static constexpr char LOBBS_LINE_TRUNC[] = "[...]"; static constexpr size_t LOBBS_LINE_TRUNC_LEN = sizeof(LOBBS_LINE_TRUNC) - 1; static size_t lobbsHumanLineLen(const std::string &line, size_t maxBytes) { size_t len = strlen(line.c_str()); if (len <= maxBytes) return len; return maxBytes <= LOBBS_LINE_TRUNC_LEN ? 0 : maxBytes; } static void lobbsHumanAppendLine(std::string &out, const std::string &line, size_t maxBytes) { size_t len = strlen(line.c_str()); if (len <= maxBytes) { out.append(line, 0, len); return; } if (maxBytes <= LOBBS_LINE_TRUNC_LEN) return; out.append(line, 0, maxBytes - LOBBS_LINE_TRUNC_LEN); out += LOBBS_LINE_TRUNC; } static bool lobbsHumanPackPages(const std::vector &lines, size_t maxBytes, bool reserveFooter, uint32_t *pageStarts, int &pageCountOut) { pageCountOut = 0; if (lines.empty()) return false; uint32_t idx = 0; while (idx < lines.size() && pageCountOut < LOBBS_PAGER_MAX_PAGES) { pageStarts[pageCountOut] = idx; size_t used = 0; while (idx < lines.size()) { size_t footerReserve = reserveFooter ? LOBBS_PAGER_FOOTER_MAX : 0; size_t maxLine = maxBytes > footerReserve ? maxBytes - footerReserve : 0; size_t lineLen = lobbsHumanLineLen(lines[idx], maxLine); size_t add = lineLen + (used > 0 ? 1 : 0); if (used + add + footerReserve > maxBytes) { if (used > 0) break; used += lineLen; idx++; break; } used += add; idx++; if (idx < lines.size()) { size_t nextAdd = lobbsHumanLineLen(lines[idx], maxLine) + 1; if (used + nextAdd + footerReserve > maxBytes) break; } } pageCountOut++; } if (pageCountOut < LOBBS_PAGER_MAX_PAGES) pageStarts[pageCountOut] = (uint32_t)lines.size(); return pageCountOut > 0; } bool lobbsPaginatePlainText(const std::string &text, uint32_t page1, std::string &pageOut, const char **errMsg) { pageOut.clear(); if (text.empty()) { if (errMsg) *errMsg = "Empty."; return false; } std::vector lines; size_t start = 0; while (start <= text.size()) { size_t end = text.find('\n', start); if (end == std::string::npos) { lines.push_back(text.substr(start)); break; } lines.push_back(text.substr(start, end - start)); start = end + 1; } if (page1 == 0) page1 = 1; uint32_t pageStarts[LOBBS_PAGER_MAX_PAGES + 1]; memset(pageStarts, 0, sizeof(pageStarts)); int pageCount = 0; const size_t pageBytes = lobbsPlatformMaxReplyBytes(); lobbsHumanPackPages(lines, pageBytes, true, pageStarts, pageCount); if (pageCount <= 0) { if (errMsg) *errMsg = "Empty."; return false; } if (pageCount == 1) { lobbsHumanPackPages(lines, pageBytes, false, pageStarts, pageCount); pageStarts[1] = (uint32_t)lines.size(); } if (page1 > (uint32_t)pageCount) { if (errMsg) *errMsg = "No such page."; return false; } uint32_t from = pageStarts[page1 - 1]; uint32_t to = pageStarts[page1]; size_t footerReserve = pageCount > 1 ? LOBBS_PAGER_FOOTER_MAX : 0; size_t maxLine = pageBytes > footerReserve ? pageBytes - footerReserve : 0; for (uint32_t i = from; i < to; i++) { if (i > from) pageOut.push_back('\n'); lobbsHumanAppendLine(pageOut, lines[i], maxLine); } if ((uint32_t)pageCount > 1) { char footer[LOBBS_PAGER_FOOTER_MAX]; snprintf(footer, sizeof(footer), "{p %u/%u}", page1, (uint32_t)pageCount); pageOut.push_back('\n'); pageOut += footer; } return true; }