feat(meshtastic): extract LoBBS plugin library

This commit is contained in:
Ben Allfree committed 2026-10-06 23:04:05 -07:00
1 parent 1f7ded8f3f
commit 5bffe8d259
124 files changed
+10907 -39

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
#if !MESHTASTIC_EXCLUDE_LOBBS
#include "paginate.h"
#include "LoBBSReply.h"
#include <cstdio>
#include "LoBBSStackGuard.h"
bool lobbsPaginateMachine(uint32_t reqId, const std::string &document, uint32_t page1, std::string &pageOut, const char **errMsg)
{
pageOut.clear();
if (page1 == 0)
page1 = 1;
char prefix[40];
size_t prefixLen = (size_t)snprintf(prefix, sizeof(prefix), "<%u>ok\n", reqId);
if (prefixLen + document.size() <= LOBBS_REPLY_BYTES) {
if (page1 > 1) {
if (errMsg)
*errMsg = "No such page.";
return false;
}
pageOut = prefix;
pageOut += document;
return true;
}
// Size every page for the widest header at this digit count; widen until the page count fits.
uint32_t widest = 9;
size_t budget = 0;
uint32_t pages = 0;
while (true) {
prefixLen = (size_t)snprintf(prefix, sizeof(prefix), "<%u>ok [%u:%u]\n", reqId, widest, widest);
budget = LOBBS_REPLY_BYTES - prefixLen;
pages = (uint32_t)((document.size() + budget - 1) / budget);
if (pages <= widest)
break;
widest = widest * 10 + 9;
}
if (page1 > pages) {
if (errMsg)
*errMsg = "No such page.";
return false;
}
snprintf(prefix, sizeof(prefix), "<%u>ok [%u:%u]\n", reqId, page1, pages);
pageOut = prefix;
pageOut.append(document, (size_t)(page1 - 1) * budget, budget);
return true;
}
#endif
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#if !MESHTASTIC_EXCLUDE_LOBBS
#include <stdint.h>
#include <string>
bool lobbsPaginateMachine(uint32_t reqId, const std::string &document, uint32_t page1, std::string &pageOut, const char **errMsg);
#endif
+26
View File
@@ -0,0 +1,26 @@
#if !MESHTASTIC_EXCLUDE_LOBBS
#include "serialize.h"
#include <cstddef>
#include "LoBBSStackGuard.h"
bool lobbsSerializeMachine(const LoBBSResponse &resp, std::string &out)
{
out.clear();
if (!resp.ok) {
out = resp.error;
return true;
}
for (const LoScalar &rec : resp.records) {
if (!out.empty())
out.push_back('\n');
std::string line;
if (!rec.encode(line, SIZE_MAX))
return false;
out += line;
}
return true;
}
#endif
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#if !MESHTASTIC_EXCLUDE_LOBBS
#include "LoBBSResponse.h"
#include <string>
bool lobbsSerializeMachine(const LoBBSResponse &resp, std::string &out);
#endif
+134
View File
@@ -0,0 +1,134 @@
#if !MESHTASTIC_EXCLUDE_LOBBS
#include "paginate.h"
#include "LoBBSReply.h"
#include <cstdio>
#include <cstring>
#include <vector>
#include "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<std::string> &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<std::string> 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;
lobbsHumanPackPages(lines, LOBBS_REPLY_BYTES, true, pageStarts, pageCount);
if (pageCount <= 0) {
if (errMsg)
*errMsg = "Empty.";
return false;
}
if (pageCount == 1) {
lobbsHumanPackPages(lines, LOBBS_REPLY_BYTES, 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 = LOBBS_REPLY_BYTES > footerReserve ? LOBBS_REPLY_BYTES - 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;
}
#endif
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#if !MESHTASTIC_EXCLUDE_LOBBS
#include <stdint.h>
#include <string>
bool lobbsPaginatePlainText(const std::string &text, uint32_t page1, std::string &pageOut, const char **errMsg);
#endif
+54
View File
@@ -0,0 +1,54 @@
#if !MESHTASTIC_EXCLUDE_LOBBS
#include "serialize.h"
#include "LoBBSHooks.h"
#include <lodb/LoDB.h>
#include "LoBBSStackGuard.h"
static void lobbsDisplayGeneric(const LoScalar &record, std::string &lineOut)
{
std::string title;
std::string body;
record.getString(LODB_F_TITLE, title);
record.getString(LODB_F_DESCRIPTION, body);
if (title.empty() && body.empty()) {
lineOut.clear();
return;
}
if (title.empty()) {
lineOut = body;
return;
}
if (body.empty()) {
lineOut = title;
return;
}
lineOut = title + " (" + body + ")";
}
bool lobbsSerializePlainText(const LoBBSCommandCtx &ctx, const LoBBSResponse &resp, std::string &out)
{
out.clear();
if (!resp.ok) {
out = resp.error;
return !out.empty();
}
for (const LoScalar &rec : resp.records) {
std::string line;
lobbsDisplayGeneric(rec, line);
LoScalar shown;
shown.setString(LODB_F_TITLE, line);
lobbsApplyFilter("display_human", const_cast<LoBBSCommandCtx &>(ctx), shown, rec);
line.clear();
shown.getString(LODB_F_TITLE, line);
if (line.empty())
continue;
if (!out.empty())
out.push_back('\n');
out += line;
}
return !out.empty();
}
#endif
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#if !MESHTASTIC_EXCLUDE_LOBBS
#include "LoBBSCommandCtx.h"
#include "LoBBSResponse.h"
#include <string>
bool lobbsSerializePlainText(const LoBBSCommandCtx &ctx, const LoBBSResponse &resp, std::string &out);
#endif