summaryrefslogtreecommitdiff
path: root/src/misc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/misc.cpp')
-rw-r--r--src/misc.cpp180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/misc.cpp b/src/misc.cpp
index 73a6fd9bd..0a6a38488 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -18,6 +18,7 @@
#include "language.h"
#include "regexpr.h"
#include "sockets.h"
+#include "textproc.h"
#include <cerrno>
#include <climits>
@@ -263,6 +264,185 @@ void InfoFormatter::AddOption(const Anope::string &opt)
*optstr += Language::Translate(nc, opt.c_str());
}
+TextSplitter::TextSplitter(const Anope::string &t)
+ : text(t)
+{
+}
+
+bool TextSplitter::GetLine(Anope::string &out)
+{
+ out.clear();
+ if (text.empty())
+ return false;
+
+ // Start by copying all of the formatting from the previous line
+ // onto this one.
+ for (const auto &fmt : formatting)
+ out.append(fmt);
+
+ // The maximum length of a line.
+ const auto max_length = Config->GetBlock("options").Get<size_t>("linelength", "120");
+
+ // The current printable length of the output.
+ size_t current_length = 0;
+
+ // Whether a newline was encountered or we hit the max line length.
+ bool forced_linebreak = false;
+
+ // The index of the last space we can split on.
+ size_t last_space = 0;
+
+ // Formatting which has been seen since the last space.
+ std::vector<Anope::string> uncertain_formatting;
+
+ size_t idx = 0;
+ auto toggle_formatting = [this, &idx, &uncertain_formatting](const Anope::string &fmt)
+ {
+ auto it = std::find_if(formatting.begin(), formatting.end(), [&fmt](const auto& f) {
+ return f[0] == fmt[0];
+ });
+ if (it == formatting.end())
+ {
+ formatting.push_back(fmt);
+ uncertain_formatting.push_back(fmt);
+ }
+ else
+ {
+ formatting.erase(it);
+ uncertain_formatting.push_back(*it);
+ }
+ };
+
+ for (idx = 0; idx < text.length(); ++idx)
+ {
+ if (current_length >= max_length)
+ {
+ for (const auto &uf : uncertain_formatting)
+ toggle_formatting(uf);
+
+ forced_linebreak = true;
+ break; // Max length reached.
+ }
+
+ auto chr = text[idx];
+ switch (chr)
+ {
+ case '\x02': // IRC bold
+ case '\x1D': // IRC italic
+ case '\x11': // IRC monospace
+ case '\x16': // IRC reverse
+ case '\x1E': // IRC strikethrough
+ case '\x1F': // IRC underline
+ {
+ // These formatting characters are a simple toggle.
+ toggle_formatting(chr);
+ out.push_back(chr);
+ break;
+ }
+
+ case '\x03': // Color
+ {
+ const auto start = idx;
+ while (++idx < text.length() && idx - start < 6)
+ {
+ chr = text[idx];
+ if (chr != ',' && (chr < '0' || chr > '9'))
+ {
+ idx--;
+ break;
+ }
+ }
+
+ auto color = text.substr(start, start - idx);
+ toggle_formatting(color);
+ out.append(color);
+ break;
+ }
+ case '\x04': // Hex color
+ {
+ const auto start = idx;
+ while (++idx < text.length() && idx - start < 14)
+ {
+ chr = text[idx];
+ if (chr != ',' && (chr < '0' || chr > '9') && (chr < 'A' || chr > 'F') && (chr < 'a' || chr > 'f'))
+ {
+ idx--;
+ break;
+ }
+
+ auto color = text.substr(start, start - idx);
+ toggle_formatting(color);
+ out.append(color);
+ }
+
+ break;
+ }
+
+ case '\x0A': // Forced newline (line feed)
+ case '\x0D': // Forced newline (carriage return)
+ {
+ formatting.clear();
+ last_space = idx;
+ forced_linebreak = true;
+ break;
+ }
+
+ case '\x0F': // IRC reset.
+ {
+ formatting.clear();
+ out.push_back(chr);
+ break;
+ }
+
+ case '\x1A': // Non-breaking space
+ {
+ // There aren't any single byte non-breaking spaces so we use
+ // a substitute for that purpose.
+ current_length++;
+ out.push_back(' ');
+ break;
+ }
+
+ case '\x20': // Breaking space.
+ {
+
+ // Unlike above we can split on this.
+ last_space = idx;
+ uncertain_formatting.clear();
+ current_length++;
+ out.push_back(' ');
+ break;
+ }
+
+ default: // Non-formatting character.
+ current_length++;
+ out.push_back(chr);
+ break;
+ }
+
+ if (forced_linebreak)
+ break;
+ }
+
+ if (forced_linebreak)
+ {
+ if (!last_space)
+ last_space = idx;
+
+ text.erase(0, last_space + 1);
+ out.erase(last_space);
+ }
+ else
+ {
+ // We either reached to the end of the text without needing to line wrap or
+ // we encountered a word so big that it couldn't be linewrapped.
+ text.clear();
+ }
+
+ return true;
+}
+
+
bool Anope::IsFile(const Anope::string &filename)
{
struct stat fileinfo;