diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c
index 22e3705f4c..0d8c285a1d 100644
--- a/lib/curl_get_line.c
+++ b/lib/curl_get_line.c
@@ -41,17 +41,41 @@ char *Curl_get_line(char *buf, int len, FILE *input)
bool partial = FALSE;
while(1) {
char *b = fgets(buf, len, input);
+
if(b) {
size_t rlen = strlen(b);
- if(rlen && (b[rlen-1] == '\n')) {
+
+ if(!rlen)
+ break;
+
+ if(b[rlen-1] == '\n') {
+ /* b is \n terminated */
if(partial) {
partial = FALSE;
continue;
}
return b;
}
- /* read a partial, discard the next piece that ends with newline */
- partial = TRUE;
+ else if(feof(input)) {
+ if(partial)
+ /* Line is already too large to return, ignore rest */
+ break;
+
+ if(rlen + 1 < (size_t) len) {
+ /* b is EOF terminated, insert missing \n */
+ b[rlen] = '\n';
+ b[rlen + 1] = '\0';
+ return b;
+ }
+ else
+ /* Maximum buffersize reached + EOF
+ * This line is impossible to add a \n to so we'll ignore it
+ */
+ break;
+ }
+ else
+ /* Maximum buffersize reached */
+ partial = TRUE;
}
else
break;
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 0e2ac5a595..d7194376eb 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -250,4 +250,5 @@ test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 \
test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 \
test3024 test3025 test3026 test3027 test3028 \
\
-test3100 test3101
+test3100 test3101 \
+test3200
diff --git a/tests/data/test3200 b/tests/data/test3200
new file mode 100644
index 0000000000..0904cd6d6e
--- /dev/null
+++ b/tests/data/test3200
@@ -0,0 +1,22 @@
+
+
+
+unittest
+curl_get_line
+
+
+
+#
+# Client-side
+
+
+none
+
+
+unittest
+
+
+curl_get_line unit tests
+
+
+
diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc
index c38bc954d4..3d8d7d57e7 100644
--- a/tests/unit/Makefile.inc
+++ b/tests/unit/Makefile.inc
@@ -37,7 +37,8 @@ UNITPROGS = unit1300 unit1302 unit1303 unit1304 unit1305 unit1307 \
unit1608 unit1609 unit1610 unit1611 unit1612 unit1614 \
unit1620 unit1621 \
unit1650 unit1651 unit1652 unit1653 unit1654 unit1655 \
- unit1660 unit1661
+ unit1660 unit1661 \
+ unit3200
unit1300_SOURCES = unit1300.c $(UNITFILES)
unit1300_CPPFLAGS = $(AM_CPPFLAGS)
@@ -162,3 +163,6 @@ unit1660_CPPFLAGS = $(AM_CPPFLAGS)
unit1661_SOURCES = unit1661.c $(UNITFILES)
unit1661_CPPFLAGS = $(AM_CPPFLAGS)
+
+unit3200_SOURCES = unit3200.c $(UNITFILES)
+unit3200_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/tests/unit/unit3200.c b/tests/unit/unit3200.c
new file mode 100644
index 0000000000..999b9caa52
--- /dev/null
+++ b/tests/unit/unit3200.c
@@ -0,0 +1,164 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curlcheck.h"
+#include "curl_get_line.h"
+
+#define TESTINPUT "log/curl_get_line2101"
+
+/* The test XML does not supply a way to write files without newlines
+ * so we write our own
+ */
+
+#define C64 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+#define C256 C64 C64 C64 C64
+#define C1024 C256 C256 C256 C256
+#define C4096 C1024 C1024 C1024 C1024
+
+static CURLcode unit_setup(void)
+{
+ return CURLE_OK;
+}
+
+static CURLcode unit_stop(void)
+{
+ return CURLE_OK;
+}
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Woverlength-strings"
+#endif
+
+#define NUMTESTS 6
+static const char *filecontents[] = {
+ /* Both should be read */
+ "LINE1\n"
+ "LINE2 NEWLINE\n",
+
+ /* Both should be read */
+ "LINE1\n"
+ "LINE2 NONEWLINE",
+
+ /* Only first should be read */
+ "LINE1\n"
+ C4096,
+
+ /* First line should be read */
+ "LINE1\n"
+ C4096 "SOME EXTRA TEXT",
+
+ /* First and third line should be read */
+ "LINE1\n"
+ C4096 "SOME EXTRA TEXT\n"
+ "LINE3\n",
+
+ "LINE1\x1aTEST"
+};
+
+#ifdef __GNUC__
+#pragma GCC diagnostic warning "-Woverlength-strings"
+#endif
+
+
+UNITTEST_START
+{
+ size_t i;
+ for(i = 0; i < NUMTESTS; i++) {
+ FILE *fp;
+ char buf[4096];
+ int len = 4096;
+ char *line;
+
+ fp = fopen(TESTINPUT, "wb");
+ abort_unless(fp != NULL, "Cannot open testfile");
+ fwrite(filecontents[i], 1, strlen(filecontents[i]), fp);
+ fclose(fp);
+
+ fp = fopen(TESTINPUT, "rb");
+ abort_unless(fp != NULL, "Cannot open testfile");
+
+ fprintf(stderr, "Test %d...", i);
+ switch(i) {
+ case 0:
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE1\n", line),
+ "First line failed (1)");
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE2 NEWLINE\n", line),
+ "Second line failed (1)");
+ line = Curl_get_line(buf, len, fp);
+ abort_unless(line == NULL, "Missed EOF (1)");
+ break;
+ case 1:
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE1\n", line),
+ "First line failed (2)");
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE2 NONEWLINE\n", line),
+ "Second line failed (2)");
+ line = Curl_get_line(buf, len, fp);
+ abort_unless(line == NULL, "Missed EOF (2)");
+ break;
+ case 2:
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE1\n", line),
+ "First line failed (3)");
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line == NULL,
+ "Did not detect max read on EOF (3)");
+ break;
+ case 3:
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE1\n", line),
+ "First line failed (4)");
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line == NULL,
+ "Did not ignore partial on EOF (4)");
+ break;
+ case 4:
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE1\n", line),
+ "First line failed (5)");
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE3\n", line),
+ "Third line failed (5)");
+ line = Curl_get_line(buf, len, fp);
+ abort_unless(line == NULL, "Missed EOF (5)");
+ break;
+ case 5:
+ line = Curl_get_line(buf, len, fp);
+ fail_unless(line && !strcmp("LINE1\x1aTEST\n", line),
+ "Missed/Misinterpreted ^Z (6)");
+ line = Curl_get_line(buf, len, fp);
+ abort_unless(line == NULL, "Missed EOF (6)");
+ break;
+ default:
+ abort_unless(1, "Unknown case");
+ break;
+ }
+ fclose(fp);
+ fprintf(stderr, "OK\n");
+ }
+ return 0;
+}
+UNITTEST_STOP