From: Rob de Wit Date: Wed, 23 Nov 2022 13:56:39 +0000 (+0100) Subject: curl_get_line: allow last line without newline char X-Git-Tag: curl-7_87_0~78 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=73c4f9696adc77a47a79f215de99a00fe0dbee5a;p=thirdparty%2Fcurl.git curl_get_line: allow last line without newline char improve backwards compatibility Test 3200 verifies Closes #9973 --- 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