summaryrefslogtreecommitdiff
path: root/src/logTypes.cpp
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2025-08-21 20:39:52 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2025-08-25 16:00:59 +0100
commitb2416925f8845b70ed25fb4ec7cde8ef11e8c239 (patch)
tree9ed898937ddceca6bcf0e2a6d6dfda3754dceefe /src/logTypes.cpp
downloadwebstat-b2416925f8845b70ed25fb4ec7cde8ef11e8c239.tar.bz2
webstat-b2416925f8845b70ed25fb4ec7cde8ef11e8c239.tar.xz
webstat-b2416925f8845b70ed25fb4ec7cde8ef11e8c239.zip
Initial commit; basic Apache log parsing
Diffstat (limited to 'src/logTypes.cpp')
-rw-r--r--src/logTypes.cpp96
1 files changed, 96 insertions, 0 deletions
diff --git a/src/logTypes.cpp b/src/logTypes.cpp
new file mode 100644
index 0000000..42f0979
--- /dev/null
+++ b/src/logTypes.cpp
@@ -0,0 +1,96 @@
+#include "logTypes.hpp"
+
+namespace scn {
+ scan_expected<typename ContextType::iterator>
+ scanner<WebStat::QuotedString>::scan(WebStat::QuotedString & value, ContextType & ctx)
+ {
+ if (auto empty = scn::scan<>(ctx.range(), R"("")")) {
+ return empty->begin();
+ }
+
+ auto result = scn::scan<std::string>(ctx.range(), R"("{:[^"]}")");
+ if (!result) {
+ return unexpected(result.error());
+ }
+ value = result->value();
+ return result->begin();
+ }
+
+ scan_expected<typename ContextType::iterator>
+ scanner<WebStat::QueryString>::scan(WebStat::QueryString & value, ContextType & ctx)
+ {
+ if (auto null = scn::scan<>(ctx.range(), R"("")")) {
+ return null->begin();
+ }
+
+ if (auto empty = scn::scan<>(ctx.range(), R"("?")")) {
+ value.emplace();
+ return empty->begin();
+ }
+
+ auto result = scn::scan<std::string>(ctx.range(), R"("?{:[^"]}")");
+ if (!result) {
+ return unexpected(result.error());
+ }
+ value = result->value();
+ return result->begin();
+ }
+
+ scan_expected<typename ContextType::iterator>
+ scanner<WebStat::CLFString>::scan(WebStat::CLFString & value, ContextType & ctx)
+ {
+ if (auto empty = scn::scan<>(ctx.range(), R"("")")) {
+ value.emplace();
+ return empty->begin();
+ }
+
+ if (auto null = scn::scan<>(ctx.range(), R"("-")")) {
+ return null->begin();
+ }
+
+ auto result = scn::scan<std::string>(ctx.range(), R"("{:[^"]}")");
+ if (!result) {
+ return unexpected(result.error());
+ }
+ value = result->value();
+ decode(*value);
+ return result->begin();
+ }
+
+ void
+ scanner<WebStat::CLFString>::decode(std::string & value)
+ {
+ static constexpr auto BS_MAP = []() {
+ std::array<char, 128> map {};
+ map['f'] = '\f';
+ map['n'] = '\n';
+ map['r'] = '\r';
+ map['t'] = '\t';
+ map['v'] = '\v';
+ map['"'] = '"';
+ map['\\'] = '\\';
+ return map;
+ }();
+
+ if (auto src = std::ranges::find(value, '\\'); src != value.end()) {
+ auto dest = src;
+ while (src != value.cend()) {
+ if (*src == '\\') {
+ const std::string_view escaped {++src, value.end()};
+ if (auto chr = BS_MAP[static_cast<unsigned char>(*src)]) {
+ *dest++ = chr;
+ src++;
+ }
+ else if (auto hex = scn::scan<unsigned char>(escaped, R"(x{:.2x})")) {
+ *dest++ = static_cast<char>(hex->value());
+ src += 3;
+ }
+ }
+ else {
+ *dest++ = *src++;
+ }
+ }
+ value.erase(dest, value.end());
+ }
+ }
+}