summaryrefslogtreecommitdiff
path: root/src/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/io')
-rw-r--r--src/io/cxxstdio.cpp26
-rw-r--r--src/io/cxxstdio_enums.cpp26
-rw-r--r--src/io/cxxstdio_enums.hpp77
-rw-r--r--src/io/dir.hpp2
-rw-r--r--src/io/extract.cpp220
-rw-r--r--src/io/extract.hpp234
-rw-r--r--src/io/extract_test.cpp452
-rw-r--r--src/io/fd.cpp5
-rw-r--r--src/io/fd.hpp2
-rw-r--r--src/io/fwd.hpp13
-rw-r--r--src/io/line.cpp107
-rw-r--r--src/io/line.hpp46
-rw-r--r--src/io/line_test.cpp66
-rw-r--r--src/io/read.cpp47
-rw-r--r--src/io/read.hpp14
-rw-r--r--src/io/read_test.cpp67
-rw-r--r--src/io/span.cpp113
-rw-r--r--src/io/span.hpp92
-rw-r--r--src/io/tty.cpp27
-rw-r--r--src/io/write.cpp2
-rw-r--r--src/io/write.hpp2
21 files changed, 1361 insertions, 279 deletions
diff --git a/src/io/cxxstdio.cpp b/src/io/cxxstdio.cpp
deleted file mode 100644
index ca4e880..0000000
--- a/src/io/cxxstdio.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "cxxstdio.hpp"
-// cxxstdio.cpp - pass C++ types through printf
-//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/io/cxxstdio_enums.cpp b/src/io/cxxstdio_enums.cpp
deleted file mode 100644
index 216da1d..0000000
--- a/src/io/cxxstdio_enums.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "cxxstdio_enums.hpp"
-// cxxstdio_enums.cpp - Opt-in integer formatting support for enums.
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-} // namespace tmwa
diff --git a/src/io/cxxstdio_enums.hpp b/src/io/cxxstdio_enums.hpp
deleted file mode 100644
index 05cdcae..0000000
--- a/src/io/cxxstdio_enums.hpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-// cxxstdio_enums.hpp - Opt-in integer formatting support for enums.
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "fwd.hpp"
-
-#include <cstdint>
-
-#include "../generic/enum.hpp"
-
-
-namespace tmwa
-{
-namespace e
-{
-enum class BF : uint16_t;
-enum class EPOS : uint16_t;
-enum class MapCell : uint8_t;
-enum class Option : uint16_t;
-
-inline
-auto decay_for_printf(BF v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(EPOS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(MapCell v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(Option v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-}
-
-namespace magic
-{
-enum class SPELLARG : uint8_t;
-
-inline
-auto decay_for_printf(SPELLARG v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-}
-
-enum class BL : uint8_t;
-enum class ByteCode : uint8_t;
-enum class ItemLook : uint16_t;
-enum class MS : uint8_t;
-enum class SP : uint16_t;
-enum class SkillID : uint16_t;
-enum class StatusChange : uint16_t;
-
-inline
-auto decay_for_printf(BL v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(ByteCode v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(ItemLook v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(MS v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(SP v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(SkillID v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-inline
-auto decay_for_printf(StatusChange v) -> typename remove_enum<decltype(v)>::type { return typename remove_enum<decltype(v)>::type(v); }
-} // namespace tmwa
diff --git a/src/io/dir.hpp b/src/io/dir.hpp
index 071f309..f6fedbf 100644
--- a/src/io/dir.hpp
+++ b/src/io/dir.hpp
@@ -20,8 +20,6 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
-
#include "fd.hpp"
diff --git a/src/io/extract.cpp b/src/io/extract.cpp
new file mode 100644
index 0000000..fce4dab
--- /dev/null
+++ b/src/io/extract.cpp
@@ -0,0 +1,220 @@
+#include "extract.hpp"
+// extract.cpp - a simple, hierarchical, tokenizer
+//
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <algorithm>
+
+#include "../strings/astring.hpp"
+#include "../strings/xstring.hpp"
+#include "../strings/vstring.hpp"
+
+#include "../poison.hpp"
+
+
+// TODO also pass an io::LineSpan around.
+namespace tmwa
+{
+bool impl_extract(XString str, XString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool impl_extract(XString str, RString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool impl_extract(XString str, AString *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool impl_extract(XString str, std::chrono::nanoseconds *ns)
+{
+ std::chrono::nanoseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *ns = std::chrono::nanoseconds(rep);
+ return true;
+ }
+ if (str.endswith("ns"_s))
+ {
+ if (extract(str.xrslice_h("ns"_s.size()), &rep))
+ {
+ *ns = std::chrono::nanoseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::microseconds bigger;
+ if (extract(str, &bigger))
+ {
+ *ns = bigger;
+ return *ns == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::microseconds *us)
+{
+ std::chrono::microseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *us = std::chrono::microseconds(rep);
+ return true;
+ }
+ if (str.endswith("us"_s))
+ {
+ if (extract(str.xrslice_h("us"_s.size()), &rep))
+ {
+ *us = std::chrono::microseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::milliseconds bigger;
+ if (extract(str, &bigger))
+ {
+ *us = bigger;
+ return *us == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::milliseconds *ms)
+{
+ std::chrono::milliseconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *ms = std::chrono::milliseconds(rep);
+ return true;
+ }
+ if (str.endswith("ms"_s))
+ {
+ if (extract(str.xrslice_h("ms"_s.size()), &rep))
+ {
+ *ms = std::chrono::milliseconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::seconds bigger;
+ if (extract(str, &bigger))
+ {
+ *ms = bigger;
+ return *ms == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::seconds *s)
+{
+ std::chrono::seconds::rep rep;
+ if (extract(str, &rep))
+ {
+ *s = std::chrono::seconds(rep);
+ return true;
+ }
+ if (str.endswith("s"_s))
+ {
+ if (extract(str.xrslice_h("s"_s.size()), &rep))
+ {
+ *s = std::chrono::seconds(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::minutes bigger;
+ if (extract(str, &bigger))
+ {
+ *s = bigger;
+ return *s == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::minutes *min)
+{
+ std::chrono::minutes::rep rep;
+ if (extract(str, &rep))
+ {
+ *min = std::chrono::minutes(rep);
+ return true;
+ }
+ if (str.endswith("min"_s))
+ {
+ if (extract(str.xrslice_h("min"_s.size()), &rep))
+ {
+ *min = std::chrono::minutes(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::hours bigger;
+ if (extract(str, &bigger))
+ {
+ *min = bigger;
+ return *min == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::hours *h)
+{
+ std::chrono::hours::rep rep;
+ if (extract(str, &rep))
+ {
+ *h = std::chrono::hours(rep);
+ return true;
+ }
+ if (str.endswith("h"_s))
+ {
+ if (extract(str.xrslice_h("h"_s.size()), &rep))
+ {
+ *h = std::chrono::hours(rep);
+ return true;
+ }
+ return false;
+ }
+ std::chrono::duration<int, std::ratio<60*60*24>> bigger;
+ if (extract(str, &bigger))
+ {
+ *h = bigger;
+ return *h == bigger;
+ }
+ return false;
+}
+bool impl_extract(XString str, std::chrono::duration<int, std::ratio<60*60*24>> *d)
+{
+ std::chrono::duration<int, std::ratio<60*60*24>>::rep rep;
+ if (extract(str, &rep))
+ {
+ *d = std::chrono::duration<int, std::ratio<60*60*24>>(rep);
+ return true;
+ }
+ if (str.endswith("d"_s))
+ {
+ if (extract(str.xrslice_h("d"_s.size()), &rep))
+ {
+ *d = std::chrono::duration<int, std::ratio<60*60*24>>(rep);
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+} // namespace tmwa
diff --git a/src/io/extract.hpp b/src/io/extract.hpp
new file mode 100644
index 0000000..897f50e
--- /dev/null
+++ b/src/io/extract.hpp
@@ -0,0 +1,234 @@
+#pragma once
+// extract.hpp - a simple, hierarchical, tokenizer
+//
+// Copyright © 2012-2013 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cerrno>
+#include <cstdlib>
+
+#include <algorithm>
+#include <chrono>
+#include <vector>
+
+#include "../ints/wrap.hpp"
+
+#include "../strings/xstring.hpp"
+
+#include "../compat/time_t.hpp"
+
+#include "../generic/enum.hpp"
+
+
+namespace tmwa
+{
+template<class T>
+bool extract(XString str, T t);
+
+template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && !std::is_same<T, bool>::value>::type>
+bool impl_extract(XString str, T *iv)
+{
+ if (!str || str.size() > 20)
+ return false;
+ if (!((str.front() == '-' && std::is_signed<T>::value)
+ || ('0' <= str.front() && str.front() <= '9')))
+ return false;
+ // needs a NUL, but can't always be given one. TODO VString?
+ char buf[20 + 1];
+ std::copy(str.begin(), str.end(), buf);
+ buf[str.size()] = '\0';
+
+ char *end;
+ errno = 0;
+ if (std::is_signed<T>::value)
+ {
+ long long v = strtoll(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+ else
+ {
+ unsigned long long v = strtoull(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+}
+
+inline
+bool impl_extract(XString str, TimeT *tv)
+{
+ return extract(str, &tv->value);
+}
+
+template<class T, typename=typename std::enable_if<std::is_enum<T>::value>::type>
+bool extract_as_int(XString str, T *iv)
+{
+ typedef typename underlying_type<T>::type U;
+ U v;
+ // defer to integer version
+ if (!extract(str, &v))
+ return false;
+ // TODO check bounds using enum min/max as in SSCANF
+ *iv = static_cast<T>(v);
+ return true;
+}
+
+bool impl_extract(XString str, XString *rv);
+bool impl_extract(XString str, RString *rv);
+bool impl_extract(XString str, AString *rv);
+
+template<uint8_t N>
+bool impl_extract(XString str, VString<N> *out)
+{
+ if (str.size() > N)
+ return false;
+ *out = str;
+ return true;
+}
+
+inline
+bool impl_extract(XString str, LString exact)
+{
+ return str == exact;
+}
+
+template<class T>
+class LStripper
+{
+public:
+ T impl;
+};
+
+template<class T>
+LStripper<T> lstripping(T v)
+{
+ return {v};
+}
+
+template<class T>
+bool impl_extract(XString str, LStripper<T> out)
+{
+ return extract(str.lstrip(), out.impl);
+}
+
+// basically just a std::tuple
+// but it provides its data members publically
+template<char split, int n, class... T>
+class Record;
+template<char split, int n>
+class Record<split, n>
+{
+};
+template<char split, int n, class F, class... R>
+class Record<split, n, F, R...>
+{
+public:
+ F frist;
+ Record<split, n - 1, R...> rest;
+public:
+ Record(F f, R... r)
+ : frist(f), rest(r...)
+ {}
+};
+template<char split, class... T>
+Record<split, sizeof...(T), T...> record(T... t)
+{
+ return Record<split, sizeof...(T), T...>(t...);
+}
+template<char split, int n, class... T>
+Record<split, n, T...> record(T... t)
+{
+ static_assert(0 < n && n < sizeof...(T), "don't be silly");
+ return Record<split, n, T...>(t...);
+}
+
+template<char split, int n>
+bool impl_extract(XString str, Record<split, n>)
+{
+ return !str;
+}
+
+template<char split, int n, class F, class... R>
+bool impl_extract(XString str, Record<split, n, F, R...> rec)
+{
+ XString::iterator s = std::find(str.begin(), str.end(), split);
+ XString::iterator s2 = s;
+ if (s2 != str.end())
+ ++s2;
+ XString head = str.xislice_h(s);
+ XString tail = str.xislice_t(s2);
+ if (s == str.end())
+ return (extract(head, rec.frist) && n <= 1)
+ || (!head && n <= 0);
+
+ return (extract(head, rec.frist) || n <= 0)
+ && extract(tail, rec.rest);
+}
+
+template<char split, class T>
+struct VRecord
+{
+ std::vector<T> *arr;
+};
+
+template<char split, class T>
+VRecord<split, T> vrec(std::vector<T> *arr)
+{
+ return {arr};
+}
+
+template<char split, class T>
+bool impl_extract(XString str, VRecord<split, T> rec)
+{
+ if (!str)
+ return true;
+ XString::iterator s = std::find(str.begin(), str.end(), split);
+ rec.arr->emplace_back();
+ if (s == str.end())
+ return extract(str, &rec.arr->back());
+ return extract(str.xislice_h(s), &rec.arr->back())
+ && extract(str.xislice_t(s + 1), rec);
+}
+
+template<class R>
+bool impl_extract(XString str, Wrapped<R> *w)
+{
+ return extract(str, &w->_value);
+}
+
+bool impl_extract(XString str, std::chrono::nanoseconds *ns);
+bool impl_extract(XString str, std::chrono::microseconds *us);
+bool impl_extract(XString str, std::chrono::milliseconds *ms);
+bool impl_extract(XString str, std::chrono::seconds *s);
+bool impl_extract(XString str, std::chrono::minutes *min);
+bool impl_extract(XString str, std::chrono::hours *h);
+bool impl_extract(XString str, std::chrono::duration<int, std::ratio<60*60*24>> *d);
+
+// this must come after all non-`namespace tmwa` `impl_extract`s.
+// In particular, the ones for `*int*` and `std::chrono::*`
+template<class T>
+bool extract(XString str, T t)
+{
+ return impl_extract(str, t);
+}
+} // namespace tmwa
diff --git a/src/io/extract_test.cpp b/src/io/extract_test.cpp
new file mode 100644
index 0000000..ee4cb08
--- /dev/null
+++ b/src/io/extract_test.cpp
@@ -0,0 +1,452 @@
+#include "extract.hpp"
+// extract_test.cpp - Testsuite for a simple, hierarchical, tokenizer
+//
+// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../strings/xstring.hpp"
+
+#include "../net/timer.t.hpp"
+
+#include "../mmo/strs.hpp"
+
+#include "../high/extract_mmo.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+TEST(extract, record_int)
+{
+ int x, y, z;
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(""_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+}
+
+TEST(extract, record_str)
+{
+ XString x, y, z;
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1"_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract(" "_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract(""_s, record<' '>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1"_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract(" "_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract(""_s, record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+
+ EXPECT_FALSE(extract("1 2 3 4 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_FALSE(extract("1 2 3 4"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 3"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ("3"_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 2"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ("2"_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1 "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract("1"_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1"_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract(" "_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+ EXPECT_TRUE(extract(""_s, record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(""_s, x);
+ EXPECT_EQ(""_s, y);
+ EXPECT_EQ(""_s, z);
+ x = y = z = ""_s;
+}
+
+TEST(extract, mapname)
+{
+ MapName map;
+ EXPECT_TRUE(extract("abc"_s, &map));
+ EXPECT_EQ(map, "abc"_s);
+ EXPECT_TRUE(extract("abc.gat"_s, &map));
+ EXPECT_EQ(map, "abc"_s);
+ EXPECT_TRUE(extract("abcdefghijklmno"_s, &map));
+ EXPECT_EQ(map, "abcdefghijklmno"_s);
+ EXPECT_TRUE(extract("abcdefghijklmno.gat"_s, &map));
+ EXPECT_EQ(map, "abcdefghijklmno"_s);
+}
+
+TEST(extract, chrono)
+{
+ std::chrono::nanoseconds ns;
+ std::chrono::microseconds us;
+ std::chrono::milliseconds ms;
+ std::chrono::seconds s;
+ std::chrono::minutes min;
+ std::chrono::hours h;
+ std::chrono::duration<int, std::ratio<60*60*24>> d;
+
+ EXPECT_TRUE(extract("1"_s, &ns));
+ EXPECT_EQ(ns, 1_ns);
+ EXPECT_TRUE(extract("3ns"_s, &ns));
+ EXPECT_EQ(ns, 3_ns);
+ EXPECT_TRUE(extract("4us"_s, &ns));
+ EXPECT_EQ(ns, 4_us);
+ EXPECT_TRUE(extract("5ms"_s, &ns));
+ EXPECT_EQ(ns, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &ns));
+ EXPECT_EQ(ns, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &ns));
+ EXPECT_EQ(ns, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &ns));
+ EXPECT_EQ(ns, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &ns));
+ EXPECT_EQ(ns, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &us));
+ EXPECT_EQ(us, 1_us);
+ EXPECT_TRUE(extract("4us"_s, &us));
+ EXPECT_EQ(us, 4_us);
+ EXPECT_TRUE(extract("5ms"_s, &us));
+ EXPECT_EQ(us, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &us));
+ EXPECT_EQ(us, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &us));
+ EXPECT_EQ(us, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &us));
+ EXPECT_EQ(us, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &us));
+ EXPECT_EQ(us, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &ms));
+ EXPECT_EQ(ms, 1_ms);
+ EXPECT_TRUE(extract("5ms"_s, &ms));
+ EXPECT_EQ(ms, 5_ms);
+ EXPECT_TRUE(extract("6s"_s, &ms));
+ EXPECT_EQ(ms, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &ms));
+ EXPECT_EQ(ms, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &ms));
+ EXPECT_EQ(ms, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &ms));
+ EXPECT_EQ(ms, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &s));
+ EXPECT_EQ(s, 1_s);
+ EXPECT_TRUE(extract("6s"_s, &s));
+ EXPECT_EQ(s, 6_s);
+ EXPECT_TRUE(extract("7min"_s, &s));
+ EXPECT_EQ(s, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &s));
+ EXPECT_EQ(s, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &s));
+ EXPECT_EQ(s, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &min));
+ EXPECT_EQ(min, 1_min);
+ EXPECT_TRUE(extract("7min"_s, &min));
+ EXPECT_EQ(min, 7_min);
+ EXPECT_TRUE(extract("8h"_s, &min));
+ EXPECT_EQ(min, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &min));
+ EXPECT_EQ(min, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &h));
+ EXPECT_EQ(h, 1_h);
+ EXPECT_TRUE(extract("8h"_s, &h));
+ EXPECT_EQ(h, 8_h);
+ EXPECT_TRUE(extract("9d"_s, &h));
+ EXPECT_EQ(h, 9_d);
+
+ EXPECT_TRUE(extract("1"_s, &d));
+ EXPECT_EQ(d, 1_d);
+ EXPECT_TRUE(extract("9d"_s, &d));
+ EXPECT_EQ(d, 9_d);
+}
+} // namespace tmwa
diff --git a/src/io/fd.cpp b/src/io/fd.cpp
index c0b44e8..bb0bbd5 100644
--- a/src/io/fd.cpp
+++ b/src/io/fd.cpp
@@ -102,6 +102,11 @@ namespace io
int FD::close()
{
+ if (fd == -1)
+ {
+ errno = EBADF;
+ return -1;
+ }
return ::close(fd);
}
int FD::shutdown(int how)
diff --git a/src/io/fd.hpp b/src/io/fd.hpp
index d04d5bf..03a8b44 100644
--- a/src/io/fd.hpp
+++ b/src/io/fd.hpp
@@ -23,8 +23,6 @@
#include <sys/select.h>
#include <sys/socket.h>
-#include "../strings/fwd.hpp"
-
#include "../diagnostics.hpp"
diff --git a/src/io/fwd.hpp b/src/io/fwd.hpp
index deeb08c..3b9452b 100644
--- a/src/io/fwd.hpp
+++ b/src/io/fwd.hpp
@@ -20,6 +20,12 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+#include "../compat/fwd.hpp" // rank 2
+#include "../generic/fwd.hpp" // rank 3
+// io/fwd.hpp is rank 4
+
namespace tmwa
{
@@ -28,5 +34,12 @@ namespace io
class ReadFile;
class WriteFile;
class AppendFile;
+ class LineReader;
+ class LineCharReader;
+ class Line;
+ class LineChar;
+ class LineSpan;
+ template<class T>
+ class Spanned;
} // namespace io
} // namespace tmwa
diff --git a/src/io/line.cpp b/src/io/line.cpp
index a1cdf42..5d7e792 100644
--- a/src/io/line.cpp
+++ b/src/io/line.cpp
@@ -19,9 +19,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "../strings/astring.hpp"
-#include "../strings/mstring.hpp"
#include "../strings/zstring.hpp"
-#include "../strings/xstring.hpp"
#include "cxxstdio.hpp"
@@ -32,71 +30,6 @@ namespace tmwa
{
namespace io
{
- AString Line::message_str(ZString cat, ZString msg) const
- {
- MString out;
- if (column)
- out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
- filename, line, column, cat, msg);
- else
- out += STRPRINTF("%s:%u: %s: %s\n"_fmt,
- filename, line, cat, msg);
- out += STRPRINTF("%s\n"_fmt, text);
- out += STRPRINTF("%*c\n"_fmt, column, '^');
- return AString(out);
- }
-
- void Line::message(ZString cat, ZString msg) const
- {
- if (column)
- FPRINTF(stderr, "%s:%u:%u: %s: %s\n"_fmt,
- filename, line, column, cat, msg);
- else
- FPRINTF(stderr, "%s:%u: %s: %s\n"_fmt,
- filename, line, cat, msg);
- FPRINTF(stderr, "%s\n"_fmt, text);
- FPRINTF(stderr, "%*c\n"_fmt, column, '^');
- }
-
- AString LineSpan::message_str(ZString cat, ZString msg) const
- {
- assert (begin.column);
- assert (end.column);
- assert (begin.line < end.line || begin.column <= end.column);
-
- MString out;
- if (begin.line == end.line)
- {
- out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
- begin.filename, begin.line, begin.column, cat, msg);
- out += STRPRINTF("%s\n"_fmt, begin.text);
- out += STRPRINTF("%*c"_fmt, begin.column, '^');
- for (unsigned c = begin.column; c != end.column; ++c)
- out += '~';
- out += '\n';
- }
- else
- {
- out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
- begin.filename, begin.line, begin.column, cat, msg);
- out += STRPRINTF("%s\n"_fmt, begin.text);
- out += STRPRINTF("%*c"_fmt, begin.column, '^');
- for (unsigned c = begin.column; c != begin.text.size(); ++c)
- out += '~';
- out += " ...\n"_s;
- out += STRPRINTF("%s\n"_fmt, end.text);
- for (unsigned c = 0; c != end.column; ++c)
- out += '~';
- out += '\n';
- }
- return AString(out);
- }
-
- void LineSpan::message(ZString cat, ZString msg) const
- {
- FPRINTF(stderr, "%s"_fmt, message_str(cat, msg));
- }
-
LineReader::LineReader(ZString name)
: filename(name), line(0), column(0), rf(name)
{}
@@ -105,6 +38,14 @@ namespace io
: filename(name), line(0), column(0), rf(fd)
{}
+ LineReader::LineReader(read_file_from_string, ZString name, XString content, int startline, FD fd)
+ : filename(name), line(startline-1), column(0), rf(from_string, content, fd)
+ {}
+
+ LineReader::LineReader(read_file_from_string, ZString name, LString content, int startline, FD fd)
+ : filename(name), line(startline-1), column(0), rf(from_string, content, fd)
+ {}
+
bool LineReader::read_line(Line& l)
{
AString text;
@@ -145,6 +86,38 @@ namespace io
column = 0;
}
+ LineCharReader::LineCharReader(read_file_from_string, ZString name, XString content, int startline, int startcol, FD fd)
+ : LineReader(from_string, name, content, 1, fd)
+ {
+ column = 1; // not 0, not whole line
+ if (rf.is_open())
+ adv();
+ if (!line)
+ column = 0;
+ else
+ {
+ line = startline;
+ column = startcol;
+ line_text = STRPRINTF("%*s"_fmt, static_cast<int>(column-1 + line_text.size()), line_text);
+ }
+ }
+
+ LineCharReader::LineCharReader(read_file_from_string, ZString name, LString content, int startline, int startcol, FD fd)
+ : LineReader(from_string, name, content, 1, fd)
+ {
+ column = 1; // not 0, not whole line
+ if (rf.is_open())
+ adv();
+ if (!line)
+ column = 0;
+ else
+ {
+ line = startline;
+ column = startcol;
+ line_text = STRPRINTF("%*s"_fmt, static_cast<int>(column-1 + line_text.size()), line_text);
+ }
+ }
+
bool LineCharReader::get(LineChar& c)
{
if (!column)
diff --git a/src/io/line.hpp b/src/io/line.hpp
index 8244c5e..c94eeb9 100644
--- a/src/io/line.hpp
+++ b/src/io/line.hpp
@@ -21,55 +21,15 @@
#include "fwd.hpp"
#include "../strings/rstring.hpp"
-#include "../strings/zstring.hpp"
-#include "../strings/literal.hpp"
#include "read.hpp"
+#include "span.hpp"
namespace tmwa
{
namespace io
{
- // TODO split this out
- struct Line
- {
- RString text;
-
- RString filename;
- // 1-based
- uint16_t line, column;
-
- AString message_str(ZString cat, ZString msg) const;
- void message(ZString cat, ZString msg) const;
- void note(ZString msg) const { message("note"_s, msg); }
- void warning(ZString msg) const { message("warning"_s, msg); }
- void error(ZString msg) const { message("error"_s, msg); }
- };
-
- // psst, don't tell anyone
- struct LineChar : Line
- {
- char ch()
- {
- size_t c = column - 1;
- if (c == text.size())
- return '\n';
- return text[c];
- }
- };
-
- struct LineSpan
- {
- LineChar begin, end;
-
- AString message_str(ZString cat, ZString msg) const;
- void message(ZString cat, ZString msg) const;
- void note(ZString msg) const { message("note"_s, msg); }
- void warning(ZString msg) const { message("warning"_s, msg); }
- void error(ZString msg) const { message("error"_s, msg); }
- };
-
class LineReader
{
protected:
@@ -83,6 +43,8 @@ namespace io
LineReader& operator = (LineReader&&) = delete;
// needed for unit tests
LineReader(ZString name, FD fd);
+ LineReader(read_file_from_string, ZString name, XString content, int startline=1, FD fd=FD());
+ LineReader(read_file_from_string, ZString name, LString content, int startline=1, FD fd=FD());
bool read_line(Line& l);
bool is_open();
@@ -97,6 +59,8 @@ namespace io
LineCharReader(LineCharReader&&) = delete;
LineCharReader& operator = (LineCharReader&&) = delete;
LineCharReader(ZString name, FD fd);
+ LineCharReader(read_file_from_string, ZString name, XString content, int startline=1, int startcol=1, FD fd=FD());
+ LineCharReader(read_file_from_string, ZString name, LString content, int startline=1, int startcol=1, FD fd=FD());
bool get(LineChar& c);
void adv();
diff --git a/src/io/line_test.cpp b/src/io/line_test.cpp
index edf60bd..582ee81 100644
--- a/src/io/line_test.cpp
+++ b/src/io/line_test.cpp
@@ -23,6 +23,8 @@
#include "../strings/astring.hpp"
#include "../strings/zstring.hpp"
+#include "../tests/fdhack.hpp"
+
#include "../poison.hpp"
@@ -57,6 +59,7 @@ TEST(io, line1)
}
TEST(io, line2)
{
+ QuietFd q;
io::LineReader lr("<string2>"_s, string_pipe("Hello\nWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -73,6 +76,7 @@ TEST(io, line2)
}
TEST(io, line3)
{
+ QuietFd q;
io::LineReader lr("<string3>"_s, string_pipe("Hello\rWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -89,6 +93,7 @@ TEST(io, line3)
}
TEST(io, line4)
{
+ QuietFd q;
io::LineReader lr("<string4>"_s, string_pipe("Hello\r\nWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -105,6 +110,7 @@ TEST(io, line4)
}
TEST(io, line5)
{
+ QuietFd q;
io::LineReader lr("<string5>"_s, string_pipe("Hello\n\rWorld"_s));
io::Line hi;
EXPECT_TRUE(lr.read_line(hi));
@@ -124,6 +130,20 @@ TEST(io, line5)
EXPECT_EQ(hi.column, 0);
EXPECT_FALSE(lr.read_line(hi));
}
+TEST(io, line1text)
+{
+ io::LineReader lr(io::from_string, "<string1text>"_s, "Hello\nWorld"_s, 2);
+ io::Line hi;
+ EXPECT_TRUE(lr.read_line(hi));
+ EXPECT_EQ(hi.text, "Hello"_s);
+ EXPECT_EQ(hi.line, 2);
+ EXPECT_EQ(hi.column, 0);
+ EXPECT_TRUE(lr.read_line(hi));
+ EXPECT_EQ(hi.text, "World"_s);
+ EXPECT_EQ(hi.line, 3);
+ EXPECT_EQ(hi.column, 0);
+ EXPECT_FALSE(lr.read_line(hi));
+}
TEST(io, linechar1)
{
@@ -175,6 +195,7 @@ TEST(io, linechar1)
}
TEST(io, linechar2)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar2>"_s, string_pipe("Hi\nWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -223,6 +244,7 @@ TEST(io, linechar2)
}
TEST(io, linechar3)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar3>"_s, string_pipe("Hi\rWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -271,6 +293,7 @@ TEST(io, linechar3)
}
TEST(io, linechar4)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar4>"_s, string_pipe("Hi\r\nWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -319,6 +342,7 @@ TEST(io, linechar4)
}
TEST(io, linechar5)
{
+ QuietFd q;
io::LineCharReader lr("<stringchar5>"_s, string_pipe("Hi\n\rWu"_s));
io::LineChar c;
EXPECT_TRUE(lr.get(c));
@@ -372,6 +396,42 @@ TEST(io, linechar5)
lr.adv();
EXPECT_FALSE(lr.get(c));
}
+TEST(io, linechar1text)
+{
+ io::LineCharReader lr(io::from_string, "<stringchar1text>"_s, "Hi\nWu\n"_s, 2, 3);
+ io::LineChar c;
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'H');
+ EXPECT_EQ(c.line, 2);
+ EXPECT_EQ(c.column, 3);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'i');
+ EXPECT_EQ(c.line, 2);
+ EXPECT_EQ(c.column, 4);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), '\n');
+ EXPECT_EQ(c.line, 2);
+ EXPECT_EQ(c.column, 5);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'W');
+ EXPECT_EQ(c.line, 3);
+ EXPECT_EQ(c.column, 1);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), 'u');
+ EXPECT_EQ(c.line, 3);
+ EXPECT_EQ(c.column, 2);
+ lr.adv();
+ EXPECT_TRUE(lr.get(c));
+ EXPECT_EQ(c.ch(), '\n');
+ EXPECT_EQ(c.line, 3);
+ EXPECT_EQ(c.column, 3);
+ lr.adv();
+ EXPECT_FALSE(lr.get(c));
+}
TEST(io, linespan)
{
@@ -402,17 +462,17 @@ TEST(io, linespan)
}
while (span.end.ch() != 'r');
- EXPECT_EQ(span.begin.message_str("note"_s, "foo"_s),
+ EXPECT_EQ(span.begin.note_str("foo"_s),
"<span>:1:5: note: foo\n"
"Hello,\n"
" ^\n"_s
);
- EXPECT_EQ(span.end.message_str("warning"_s, "bar"_s),
+ EXPECT_EQ(span.end.warning_str("bar"_s),
"<span>:2:3: warning: bar\n"
"World!\n"
" ^\n"_s
);
- EXPECT_EQ(span.message_str("error"_s, "qux"_s),
+ EXPECT_EQ(span.error_str("qux"_s),
"<span>:1:5: error: qux\n"
"Hello,\n"
" ^~ ...\n"
diff --git a/src/io/read.cpp b/src/io/read.cpp
index 3ae5246..32974d6 100644
--- a/src/io/read.cpp
+++ b/src/io/read.cpp
@@ -1,7 +1,7 @@
#include "read.hpp"
// io/read.cpp - Input from files
//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -25,7 +25,7 @@
#include "../strings/zstring.hpp"
#include "../strings/literal.hpp"
-#include "../io/cxxstdio.hpp"
+#include "cxxstdio.hpp"
#include "../poison.hpp"
@@ -36,6 +36,8 @@ namespace io
{
ReadFile::ReadFile(FD f)
: fd(f), start(0), end(0)
+ // only for debug-sanity
+ , buf{}
{
}
ReadFile::ReadFile(ZString name)
@@ -46,6 +48,32 @@ namespace io
: fd(dir.open_fd(name, O_RDONLY | O_CLOEXEC)), start(0), end(0)
{
}
+ ReadFile::ReadFile(read_file_from_string, XString content, FD f)
+ : fd(f), start(0), end(), extra()
+ {
+ if (content.size() <= 4096)
+ {
+ end = content.size();
+ auto z = std::copy(content.begin(), content.end(), buf);
+ // only for debug sanity
+ std::fill(z, std::end(buf), 0);
+ return;
+ }
+ auto base = content.base();
+ if (!base)
+ {
+ extra = content;
+ end = content.size();
+ return;
+ }
+ start = &*content.begin() - &*base->begin();
+ end = &*content.end() - &*base->begin();
+ extra = *base;
+ }
+ ReadFile::ReadFile(read_file_from_string, LString content, FD f)
+ : ReadFile(from_string, RString(content), f)
+ {
+ }
ReadFile::~ReadFile()
{
fd.close();
@@ -54,6 +82,14 @@ namespace io
bool ReadFile::get(char& c)
{
+ if (extra)
+ {
+ c = extra[start];
+ ++start;
+ if (start == end)
+ extra = ""_s;
+ return true;
+ }
if (start == end)
{
if (fd == FD())
@@ -82,6 +118,7 @@ namespace io
}
bool ReadFile::getline(AString& line)
{
+ bool was_real_file = fd != FD();
MString tmp;
char c;
bool anything = false;
@@ -115,15 +152,17 @@ namespace io
else
FPRINTF(stderr, "warning: file contains bare CR\n"_fmt);
}
- else if (!happy && anything)
+ else if (!happy && anything && was_real_file)
+ {
FPRINTF(stderr, "warning: file does not contain a trailing newline\n"_fmt);
+ }
line = AString(tmp);
return anything;
}
bool ReadFile::is_open()
{
- return fd != FD();
+ return fd != FD() || start != end;
}
} // namespace io
} // namespace tmwa
diff --git a/src/io/read.hpp b/src/io/read.hpp
index 1ec26ca..2e3611b 100644
--- a/src/io/read.hpp
+++ b/src/io/read.hpp
@@ -1,7 +1,7 @@
#pragma once
// io/read.hpp - Input from files.
//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -20,7 +20,7 @@
#include "fwd.hpp"
-#include "../strings/fwd.hpp"
+#include "../strings/rstring.hpp"
#include "dir.hpp"
#include "fd.hpp"
@@ -29,18 +29,28 @@ namespace tmwa
{
namespace io
{
+ enum read_file_from_string
+ {
+ from_string,
+ };
+
+ // TODO - for internal warnings, it would be convenient if this class
+ // didn't exist at all, and instead everything was done with line info.
class ReadFile
{
private:
FD fd;
unsigned short start, end;
char buf[4096];
+ RString extra;
public:
explicit
ReadFile(FD fd);
explicit
ReadFile(ZString name);
ReadFile(const DirFd& dir, ZString name);
+ ReadFile(read_file_from_string, XString content, FD fd=FD());
+ ReadFile(read_file_from_string, LString content, FD fd=FD());
ReadFile& operator = (ReadFile&&) = delete;
ReadFile(ReadFile&&) = delete;
diff --git a/src/io/read_test.cpp b/src/io/read_test.cpp
index 8fe84b7..22c67c8 100644
--- a/src/io/read_test.cpp
+++ b/src/io/read_test.cpp
@@ -24,6 +24,8 @@
#include "../strings/zstring.hpp"
#include "../strings/literal.hpp"
+#include "../tests/fdhack.hpp"
+
#include "../poison.hpp"
@@ -47,6 +49,7 @@ io::FD string_pipe(ZString sz)
TEST(io, read1)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -63,6 +66,7 @@ TEST(io, read2)
}
TEST(io, read3)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello\r"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -71,6 +75,7 @@ TEST(io, read3)
}
TEST(io, read4)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello\r\n"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -79,6 +84,7 @@ TEST(io, read4)
}
TEST(io, read5)
{
+ QuietFd q;
io::ReadFile rf(string_pipe("Hello\n\r"_s));
AString hi;
EXPECT_TRUE(rf.getline(hi));
@@ -87,4 +93,65 @@ TEST(io, read5)
EXPECT_FALSE(hi);
EXPECT_FALSE(rf.getline(hi));
}
+
+#define S15 "0123456789abcde"_s
+#define S16 "0123456789abcdef"_s
+#define S255 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S15
+#define S256 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16 S16
+#define S4095 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S255
+#define S4096 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256 S256
+
+TEST(io, readstringr)
+{
+ LString tests[] =
+ {
+ S15,
+ S16,
+ S255,
+ S256,
+ S4095,
+ S4096,
+ S4096 S16,
+ };
+ for (RString test : tests)
+ {
+ char buf[test.size() + 1];
+
+ io::ReadFile rf(io::from_string, test);
+ EXPECT_EQ(rf.get(buf, sizeof(buf)), test.size());
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+
+ io::ReadFile rf2(io::from_string, test, string_pipe("\na"_s));
+ EXPECT_EQ(rf2.get(buf, sizeof(buf)), test.size() + 1);
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+ EXPECT_EQ('\n', buf[test.size()]);
+ }
+}
+
+TEST(io, readstringx)
+{
+ LString tests[] =
+ {
+ S15,
+ S16,
+ S255,
+ S256,
+ S4095,
+ S4096,
+ S4096 S16,
+ };
+ for (XString test : tests)
+ {
+ char buf[test.size() + 1];
+
+ io::ReadFile rf(io::from_string, test);
+ EXPECT_EQ(rf.get(buf, sizeof(buf)), test.size());
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+
+ io::ReadFile rf2(io::from_string, test, string_pipe("\na"_s));
+ EXPECT_EQ(rf2.get(buf, sizeof(buf)), test.size() + 1);
+ EXPECT_EQ(test, XString(buf + 0, buf + test.size(), nullptr));
+ EXPECT_EQ('\n', buf[test.size()]);
+ }
+}
} // namespace tmwa
diff --git a/src/io/span.cpp b/src/io/span.cpp
new file mode 100644
index 0000000..6d116c7
--- /dev/null
+++ b/src/io/span.cpp
@@ -0,0 +1,113 @@
+#include "span.hpp"
+// io/span.cpp - Tracking info about input
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "../strings/astring.hpp"
+#include "../strings/mstring.hpp"
+#include "../strings/zstring.hpp"
+
+#include "cxxstdio.hpp"
+
+#include "../poison.hpp"
+
+
+namespace tmwa
+{
+namespace io
+{
+ io::LineSpan Line::to_span() const
+ {
+ io::LineSpan rv;
+ rv.begin.text = this->text;
+ rv.begin.filename = this->filename;
+ rv.begin.line = this->line;
+ rv.begin.column = 1;
+ rv.end.text = this->text;
+ rv.end.filename = this->filename;
+ rv.end.line = this->line;
+ rv.end.column = this->text.size();
+ return rv;
+ }
+
+ AString Line::message_str(ZString cat, ZString msg) const
+ {
+ MString out;
+ if (column)
+ out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
+ filename, line, column, cat, msg);
+ else
+ out += STRPRINTF("%s:%u: %s: %s\n"_fmt,
+ filename, line, cat, msg);
+ out += STRPRINTF("%s\n"_fmt, text);
+ out += STRPRINTF("%*c\n"_fmt, column, '^');
+ return AString(out);
+ }
+
+ void Line::message(ZString cat, ZString msg) const
+ {
+ if (column)
+ FPRINTF(stderr, "%s:%u:%u: %s: %s\n"_fmt,
+ filename, line, column, cat, msg);
+ else
+ FPRINTF(stderr, "%s:%u: %s: %s\n"_fmt,
+ filename, line, cat, msg);
+ FPRINTF(stderr, "%s\n"_fmt, text);
+ FPRINTF(stderr, "%*c\n"_fmt, column, '^');
+ }
+
+ AString LineSpan::message_str(ZString cat, ZString msg) const
+ {
+ assert (begin.column);
+ assert (end.column);
+ assert (begin.line < end.line || begin.column <= end.column);
+
+ MString out;
+ if (begin.line == end.line)
+ {
+ out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
+ begin.filename, begin.line, begin.column, cat, msg);
+ out += STRPRINTF("%s\n"_fmt, begin.text);
+ out += STRPRINTF("%*c"_fmt, begin.column, '^');
+ for (unsigned c = begin.column; c != end.column; ++c)
+ out += '~';
+ out += '\n';
+ }
+ else
+ {
+ out += STRPRINTF("%s:%u:%u: %s: %s\n"_fmt,
+ begin.filename, begin.line, begin.column, cat, msg);
+ out += STRPRINTF("%s\n"_fmt, begin.text);
+ out += STRPRINTF("%*c"_fmt, begin.column, '^');
+ for (unsigned c = begin.column; c != begin.text.size(); ++c)
+ out += '~';
+ out += " ...\n"_s;
+ out += STRPRINTF("%s\n"_fmt, end.text);
+ for (unsigned c = 0; c != end.column; ++c)
+ out += '~';
+ out += '\n';
+ }
+ return AString(out);
+ }
+
+ void LineSpan::message(ZString cat, ZString msg) const
+ {
+ FPRINTF(stderr, "%s"_fmt, message_str(cat, msg));
+ }
+} // namespace io
+} // namespace tmwa
diff --git a/src/io/span.hpp b/src/io/span.hpp
new file mode 100644
index 0000000..9962b7c
--- /dev/null
+++ b/src/io/span.hpp
@@ -0,0 +1,92 @@
+#pragma once
+// io/span.hpp - Tracking info about input
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include "../strings/rstring.hpp"
+#include "../strings/zstring.hpp"
+#include "../strings/literal.hpp"
+
+
+namespace tmwa
+{
+namespace io
+{
+ // TODO split this out
+ struct Line
+ {
+ RString text;
+
+ RString filename;
+ // 1-based
+ uint16_t line, column;
+
+ AString message_str(ZString cat, ZString msg) const;
+ AString note_str(ZString msg) const { return message_str("note"_s, msg); }
+ AString warning_str(ZString msg) const { return message_str("warning"_s, msg); }
+ AString error_str(ZString msg) const { return message_str("error"_s, msg); }
+ void message(ZString cat, ZString msg) const;
+ void note(ZString msg) const { message("note"_s, msg); }
+ void warning(ZString msg) const { message("warning"_s, msg); }
+ void error(ZString msg) const { message("error"_s, msg); }
+
+ LineSpan to_span() const;
+ };
+
+ // psst, don't tell anyone
+ struct LineChar : Line
+ {
+ char ch()
+ {
+ size_t c = column - 1;
+ if (c == text.size())
+ return '\n';
+ return text[c];
+ }
+ };
+
+ struct LineSpan
+ {
+ LineChar begin, end;
+
+ AString message_str(ZString cat, ZString msg) const;
+ AString note_str(ZString msg) const { return message_str("note"_s, msg); }
+ AString warning_str(ZString msg) const { return message_str("warning"_s, msg); }
+ AString error_str(ZString msg) const { return message_str("error"_s, msg); }
+ void message(ZString cat, ZString msg) const;
+ void note(ZString msg) const { message("note"_s, msg); }
+ void warning(ZString msg) const { message("warning"_s, msg); }
+ void error(ZString msg) const { message("error"_s, msg); }
+ };
+
+ template<class T>
+ struct Spanned
+ {
+ T data;
+ LineSpan span;
+ };
+
+ template<class T>
+ Spanned<T> respan(LineSpan span, T data)
+ {
+ return Spanned<T>{std::move(data), std::move(span)};
+ }
+} // namespace io
+} // namespace tmwa
diff --git a/src/io/tty.cpp b/src/io/tty.cpp
deleted file mode 100644
index c498740..0000000
--- a/src/io/tty.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "tty.hpp"
-// io/tty.cpp - terminal escape sequences
-//
-// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
-//
-// This file is part of The Mana World (Athena server)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#include "../poison.hpp"
-
-
-namespace tmwa
-{
-/* Nothing to see here, move along */
-} // namespace tmwa
diff --git a/src/io/write.cpp b/src/io/write.cpp
index 5359c7a..a98954b 100644
--- a/src/io/write.cpp
+++ b/src/io/write.cpp
@@ -37,6 +37,8 @@ namespace io
{
WriteFile::WriteFile(FD f, bool linebuffered)
: fd(f), lb(linebuffered), buflen(0)
+ // only for debug-sanity
+ , buf{}
{}
WriteFile::WriteFile(ZString name, bool linebuffered)
: fd(FD::open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)), lb(linebuffered), buflen(0)
diff --git a/src/io/write.hpp b/src/io/write.hpp
index 1ab05f3..d7d3699 100644
--- a/src/io/write.hpp
+++ b/src/io/write.hpp
@@ -22,8 +22,6 @@
#include <cstdarg>
-#include "../strings/fwd.hpp"
-
#include "dir.hpp"
#include "fd.hpp"