fd/line: Add line buffered reader

This gives us similar functionality to std::getline but on stdplus::Fd
instances. Also more predictable behavior around newlines at the end of
a file.

Change-Id: I19039c15fa02019e4ad767ca8a1996a8417fdd86
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/fd/line.cpp b/src/fd/line.cpp
new file mode 100644
index 0000000..36c1e6f
--- /dev/null
+++ b/src/fd/line.cpp
@@ -0,0 +1,59 @@
+#include <stdplus/exception.hpp>
+#include <stdplus/fd/line.hpp>
+#include <stdplus/fd/ops.hpp>
+
+namespace stdplus
+{
+namespace fd
+{
+
+LineReader::LineReader(Fd& fd) : fd(fd)
+{
+}
+
+const std::string* LineReader::readLine()
+{
+    if (hit_eof)
+    {
+        throw exception::Eof("readLine");
+    }
+    if (line_complete)
+    {
+        line.clear();
+    }
+    while (true)
+    {
+        if (buf_data.empty())
+        {
+            try
+            {
+                buf_data = read(fd, buf);
+                if (buf_data.empty())
+                {
+                    return nullptr;
+                }
+            }
+            catch (const exception::Eof&)
+            {
+                hit_eof = true;
+                return &line;
+            }
+        }
+        line_complete = false;
+        for (size_t i = 0; i < buf_data.size(); ++i)
+        {
+            if (buf_data[i] == '\n')
+            {
+                line.insert(line.end(), buf_data.begin(), buf_data.begin() + i);
+                buf_data = buf_data.subspan(i + 1);
+                line_complete = true;
+                return &line;
+            }
+        }
+        line.insert(line.end(), buf_data.begin(), buf_data.end());
+        buf_data = {};
+    }
+}
+
+} // namespace fd
+} // namespace stdplus