changeset 77:c5f4ca941f79

Irccd: add new Irccd.File.lines function, #445 While here also remove optional '\r' in Irccd.File.readline.
author David Demelier <markand@malikania.fr>
date Wed, 30 Mar 2016 19:01:53 +0200
parents 7e9a1faeb6f6
children d58e45e3515f
files doc/html/api/module/Irccd.File/Files.cmake doc/html/api/module/Irccd.File/index.md doc/html/api/module/Irccd.File/method/lines.md doc/html/api/module/Irccd.File/method/readline.md lib/irccd/js-file.cpp lib/irccd/js-file.h tests/js-file/main.cpp
diffstat 7 files changed, 99 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/doc/html/api/module/Irccd.File/Files.cmake	Wed Mar 30 14:17:23 2016 +0200
+++ b/doc/html/api/module/Irccd.File/Files.cmake	Wed Mar 30 19:01:53 2016 +0200
@@ -28,6 +28,7 @@
 	${CMAKE_CURRENT_LIST_DIR}/method/close.md
 	${CMAKE_CURRENT_LIST_DIR}/method/constructor.md
 	${CMAKE_CURRENT_LIST_DIR}/method/dirname.md
+	${CMAKE_CURRENT_LIST_DIR}/method/lines.md
 	${CMAKE_CURRENT_LIST_DIR}/method/read.md
 	${CMAKE_CURRENT_LIST_DIR}/method/readline.md
 	${CMAKE_CURRENT_LIST_DIR}/method/remove.md
--- a/doc/html/api/module/Irccd.File/index.md	Wed Mar 30 14:17:23 2016 +0200
+++ b/doc/html/api/module/Irccd.File/index.md	Wed Mar 30 19:01:53 2016 +0200
@@ -37,6 +37,7 @@
   - [basename](method/basename.html)
   - [close](method/close.html)
   - [dirname](method/dirname.html)
+  - [lines](method/lines.html)
   - [read](method/read.html)
   - [readline](method/readline.html)
   - [remove](method/remove.html)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/html/api/module/Irccd.File/method/lines.md	Wed Mar 30 19:01:53 2016 +0200
@@ -0,0 +1,7 @@
+---
+method: lines
+summary: "Read all lines and return an array."
+synopsis: "File.prototype.lines()"
+returns: "An array with all lines."
+throws: "An [Irccd.SystemError](@baseurl@/api/module/Irccd/index.html#types) on failures."
+---
--- a/doc/html/api/module/Irccd.File/method/readline.md	Wed Mar 30 14:17:23 2016 +0200
+++ b/doc/html/api/module/Irccd.File/method/readline.md	Wed Mar 30 19:01:53 2016 +0200
@@ -5,3 +5,9 @@
 returns: "The next line or undefined if eof."
 throws: "An [Irccd.SystemError](@baseurl@/api/module/Irccd/index.html#types) on failures."
 ---
+
+## Remarks
+
+<div class="alert alert-warning" role="alert">
+**Warning**: this method is slow and its usage is discouraged on large files.
+</div>
--- a/lib/irccd/js-file.cpp	Wed Mar 30 14:17:23 2016 +0200
+++ b/lib/irccd/js-file.cpp	Wed Mar 30 19:01:53 2016 +0200
@@ -200,6 +200,15 @@
  * File methods
  * -------------------------------------------------------- */
 
+/* Remove trailing \r for CRLF line style */
+inline std::string clearCr(std::string input)
+{
+	if (input.length() > 0 && input.back() == '\r')
+		input.pop_back();
+
+	return input;
+}
+
 /*
  * Method: File.basename()
  * --------------------------------------------------------
@@ -246,6 +255,49 @@
 }
 
 /*
+ * Method: File.lines()
+ * --------------------------------------------------------
+ *
+ * Read all lines and return an array.
+ *
+ * Returns:
+ *   An array with all lines.
+ * Throws
+ *   - Any exception on error.
+ */
+duk::Ret methodLines(duk::ContextPtr ctx)
+{
+	duk::push(ctx, duk::Array{});
+
+	std::FILE *fp = duk::self<duk::Pointer<File>>(ctx)->handle();
+	std::string buffer;
+
+	char data[128];
+	int total = 0;
+
+	while (std::fgets(data, sizeof (data), fp) != nullptr) {
+		buffer += data;
+
+		auto pos = buffer.find('\n');
+
+		if (pos != std::string::npos) {
+			duk::putProperty(ctx, -1, total++, clearCr(buffer.substr(0, pos)));
+			buffer.erase(0, pos + 1);
+		}
+	}
+
+	/* Maybe an error in the stream */
+	if (std::ferror(fp))
+		duk::raise(ctx, SystemError());
+
+	/* Missing '\n' in end of file */
+	if (!buffer.empty())
+		duk::putProperty(ctx, -1, total++, clearCr(buffer));
+
+	return 1;
+}
+
+/*
  * Method: File.read(amount)
  * --------------------------------------------------------
  *
@@ -294,7 +346,7 @@
 		if (file->isClosed() || file->eof())
 			return 0;
 
-		duk::push(ctx, file->readline());
+		duk::push(ctx, clearCr(file->readline()));
 	} catch (const std::exception &) {
 		duk::raise(ctx, SystemError());
 	}
@@ -438,6 +490,7 @@
 	{ "basename",	{ methodBasename,	0	} },
 	{ "close",	{ methodClose,		0	} },
 	{ "dirname",	{ methodDirname,	0	} },
+	{ "lines",	{ methodLines,		0	} },
 	{ "read",	{ methodRead,		1	} },
 	{ "readline",	{ methodReadline,	0	} },
 	{ "remove",	{ methodRemove,		0	} },
--- a/lib/irccd/js-file.h	Wed Mar 30 14:17:23 2016 +0200
+++ b/lib/irccd/js-file.h	Wed Mar 30 19:01:53 2016 +0200
@@ -85,6 +85,11 @@
 		return m_path;
 	}
 
+	inline std::FILE *handle() noexcept
+	{
+		return m_stream;
+	}
+
 	/**
 	 * Force close, can be safely called multiple times.
 	 */
--- a/tests/js-file/main.cpp	Wed Mar 30 14:17:23 2016 +0200
+++ b/tests/js-file/main.cpp	Wed Mar 30 19:01:53 2016 +0200
@@ -213,6 +213,31 @@
 	}
 }
 
+TEST(TestJsFile, methodLines)
+{
+	duk::Context ctx;
+
+	loadJsIrccd(ctx);
+	loadJsFile(ctx);
+
+	try {
+		duk::putGlobal(ctx, "directory", IRCCD_TESTS_DIRECTORY);
+
+		auto ret = duk::pevalString(ctx,
+			"lines = new Irccd.File(directory + '/lines.txt', 'r').lines();"
+		);
+
+		if (ret != 0)
+			throw duk::error(ctx, -1);
+
+		std::vector<std::string> expected{"a", "b", "c"};
+
+		ASSERT_EQ(expected, duk::getGlobal<std::vector<std::string>>(ctx, "lines"));
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
+}
+
 TEST(TestJsFile, methodSeek1)
 {
 	duk::Context ctx;