diff --git a/lib/gis/getl.c b/lib/gis/getl.c
index 7daae816a70..34df9b04ad0 100644
--- a/lib/gis/getl.c
+++ b/lib/gis/getl.c
@@ -19,8 +19,7 @@
* \brief Gets a line of text from a file
*
* This routine runs fgets() to fetch a line of text from a file
- * (advancing file pointer) and removes trailing newline. fgets() does
- * not recognize '\\r
' as an EOL and will read past * it.
+ * (advancing file pointer) and removes trailing newline.
*
* \param buf string buffer to receive read data
* \param n maximum number of bytes to read
@@ -28,23 +27,18 @@
*
* \return 1 on success
* \return 0 EOF
+ *
+ * \see G_getl2()
*/
int G_getl(char *buf, int n, FILE *fd)
{
- if (!fgets(buf, n, fd))
- return 0;
-
- for (; *buf && *buf != '\n'; buf++)
- ;
- *buf = 0;
-
- return 1;
+ return G_getl2(buf, n, fd);
}
/*!
* \brief Gets a line of text from a file of any pedigree
*
- * This routine is like G_getl() but is more portable. It supports
+ * This routine supports
* text files created on various platforms (UNIX, MacOS9, DOS),
* i.e. \\n (\\012)
, \\r (\\015)
, and
* \\r\\n (\\015\\012)
style newlines.
diff --git a/lib/gis/testsuite/test_gis_lib_getl.py b/lib/gis/testsuite/test_gis_lib_getl.py
new file mode 100644
index 00000000000..98092955ab2
--- /dev/null
+++ b/lib/gis/testsuite/test_gis_lib_getl.py
@@ -0,0 +1,81 @@
+"""Test of gis library line reading functions
+
+@author Vaclav Petras
+"""
+
+import ctypes
+import pathlib
+import platform
+import unittest
+
+import grass.lib.gis as libgis
+from grass.gunittest.case import TestCase
+from grass.gunittest.main import test
+
+
+class TestNewlinesWithGetlFunctions(TestCase):
+ """Test C functions G_getl() and G_getl2() from gis library"""
+
+ @classmethod
+ def setUpClass(cls):
+ cls.libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c"))
+ cls.libc.fopen.restype = ctypes.POINTER(libgis.FILE)
+ cls.libc.fopen.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
+ cls.file_path = pathlib.Path("test.txt")
+
+ def tearDown(self):
+ self.file_path.unlink()
+
+ def read_lines_and_assert(self, get_line_function, newline):
+ """Write and read lines and then assert they are as expected"""
+ lines = ["Line 1", "Line 2", "Line 3"]
+ with open(self.file_path, mode="w", newline=newline) as stream:
+ for line in lines:
+ # Python text newline here.
+ # The specific newline is added by the stream.
+ stream.write(f"{line}\n")
+
+ file_ptr = self.libc.fopen(str(self.file_path).encode("utf-8"), b"r")
+ if not file_ptr:
+ raise FileNotFoundError(f"Could not open file: {self.file_path}")
+
+ try:
+ buffer_size = 50
+ buffer = ctypes.create_string_buffer(buffer_size)
+
+ for line in lines:
+ get_line_function(buffer, ctypes.sizeof(buffer), file_ptr)
+ result = buffer.value.decode("utf-8") if buffer else None
+ self.assertEqual(line, result)
+ finally:
+ self.libc.fclose(file_ptr)
+
+ def test_getl_lf(self):
+ r"""Check G_getl() with LF (\n)"""
+ self.read_lines_and_assert(libgis.G_getl, "\n")
+
+ @unittest.expectedFailure
+ def test_getl_cr(self):
+ r"""Check G_getl() with CR (\r)"""
+ self.read_lines_and_assert(libgis.G_getl, "\r")
+
+ def test_getl_crlf(self):
+ r"""Check G_getl() with CRLF (\r\n)"""
+ self.read_lines_and_assert(libgis.G_getl, "\r\n")
+
+ def test_getl2_lf(self):
+ r"""Check G_getl2() with LF (\n)"""
+ self.read_lines_and_assert(libgis.G_getl2, "\n")
+
+ @unittest.expectedFailure
+ def test_getl2_cr(self):
+ r"""Check G_getl2() with CR (\r)"""
+ self.read_lines_and_assert(libgis.G_getl2, "\r")
+
+ def test_getl2_crlf(self):
+ r"""Check G_getl2() with CRLF (\r\n)"""
+ self.read_lines_and_assert(libgis.G_getl2, "\r\n")
+
+
+if __name__ == "__main__":
+ test()