From: Daniel Stenberg Date: Sun, 19 Oct 2025 11:09:42 +0000 (+0200) Subject: curl_get_line: enhance the API X-Git-Tag: rc-8_17_0-2~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=769ccb4d4261a75c8a4236f;p=thirdparty%2Fcurl.git curl_get_line: enhance the API To make sure callers can properly differentiate between errors and know cleanly when EOF happens. Updated all users and unit test 3200. Triggered by a remark by ZeroPath Closes #19140 --- diff --git a/lib/altsvc.c b/lib/altsvc.c index 449bea8528..d9933f2298 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -228,14 +228,18 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { + bool eof = FALSE; struct dynbuf buf; curlx_dyn_init(&buf, MAX_ALTSVC_LINE); - while(Curl_get_line(&buf, fp)) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); - if(curlx_str_single(&lineptr, '#')) - altsvc_add(asi, lineptr); - } + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); + if(curlx_str_single(&lineptr, '#')) + altsvc_add(asi, lineptr); + } + } while(!result && !eof); curlx_dyn_free(&buf); /* free the line buffer */ curlx_fclose(fp); } diff --git a/lib/cookie.c b/lib/cookie.c index 59a841a303..98c13e6218 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -1205,19 +1205,27 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, ci->running = FALSE; /* this is not running, this is init */ if(fp) { struct dynbuf buf; + bool eof = FALSE; + CURLcode result; curlx_dyn_init(&buf, MAX_COOKIE_LINE); - while(Curl_get_line(&buf, fp)) { - const char *lineptr = curlx_dyn_ptr(&buf); - bool headerline = FALSE; - if(checkprefix("Set-Cookie:", lineptr)) { - /* This is a cookie line, get it! */ - lineptr += 11; - headerline = TRUE; - curlx_str_passblanks(&lineptr); - } + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + bool headerline = FALSE; + if(checkprefix("Set-Cookie:", lineptr)) { + /* This is a cookie line, get it! */ + lineptr += 11; + headerline = TRUE; + curlx_str_passblanks(&lineptr); + } - Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE); - } + (void)Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, + NULL, TRUE); + /* File reading cookie failures are not propagated back to the + caller because there is no way to do that */ + } + } while(!result && !eof); curlx_dyn_free(&buf); /* free the line buffer */ /* diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c index 4b1c6c3e09..b1b4021221 100644 --- a/lib/curl_get_line.c +++ b/lib/curl_get_line.c @@ -32,63 +32,43 @@ /* The last #include file should be: */ #include "memdebug.h" -static int appendnl(struct dynbuf *buf) -{ - CURLcode result = curlx_dyn_addn(buf, "\n", 1); - if(result) - /* too long line or out of memory */ - return 0; /* error */ - return 1; /* all good */ -} +#define appendnl(b) \ + curlx_dyn_addn(buf, "\n", 1) /* - * Curl_get_line() makes sure to only return complete whole lines that end - * newlines. + * Curl_get_line() returns only complete whole lines that end with newline. + * When 'eof' is set TRUE, the last line has been read. */ -int Curl_get_line(struct dynbuf *buf, FILE *input) +CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof) { CURLcode result; char buffer[128]; curlx_dyn_reset(buf); while(1) { - char *b = fgets(buffer, sizeof(buffer), input); size_t rlen; + char *b = fgets(buffer, sizeof(buffer), input); - if(b) { - rlen = strlen(b); - - if(!rlen) - break; + *eof = feof(input); + rlen = b ? strlen(b) : 0; + if(rlen) { result = curlx_dyn_addn(buf, b, rlen); if(result) /* too long line or out of memory */ - return 0; /* error */ - - else if(b[rlen-1] == '\n') - /* end of the line */ - return 1; /* all good */ - - else if(feof(input)) - /* append a newline */ - return appendnl(buf); - } - else { - rlen = curlx_dyn_len(buf); - if(rlen) { - b = curlx_dyn_ptr(buf); - - if(b[rlen-1] != '\n') - /* append a newline */ - return appendnl(buf); - - return 1; /* all good */ - } - else - break; + return result; } + /* now check the full line */ + rlen = curlx_dyn_len(buf); + b = curlx_dyn_ptr(buf); + if(rlen && (b[rlen-1] == '\n')) + /* LF at end of the line */ + return CURLE_OK; /* all good */ + if(*eof) + /* append a newline */ + return appendnl(buf); + /* otherwise get next line to append */ } - return 0; + return CURLE_FAILED_INIT; } #endif /* if not disabled */ diff --git a/lib/curl_get_line.h b/lib/curl_get_line.h index d4877123f2..176d5f7a30 100644 --- a/lib/curl_get_line.h +++ b/lib/curl_get_line.h @@ -27,6 +27,6 @@ #include "curlx/dynbuf.h" /* Curl_get_line() returns complete lines that end with a newline. */ -int Curl_get_line(struct dynbuf *buf, FILE *input); +CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof); #endif /* HEADER_CURL_GET_LINE_H */ diff --git a/lib/hsts.c b/lib/hsts.c index 28989764b9..4e41155f30 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -526,20 +526,24 @@ static CURLcode hsts_load(struct hsts *h, const char *file) fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { struct dynbuf buf; + bool eof = FALSE; curlx_dyn_init(&buf, MAX_HSTS_LINE); - while(Curl_get_line(&buf, fp)) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); - - /* - * Skip empty or commented lines, since we know the line will have a - * trailing newline from Curl_get_line we can treat length 1 as empty. - */ - if((*lineptr == '#') || strlen(lineptr) <= 1) - continue; - - hsts_add(h, lineptr); - } + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); + + /* + * Skip empty or commented lines, since we know the line will have a + * trailing newline from Curl_get_line we can treat length 1 as empty. + */ + if((*lineptr == '#') || strlen(lineptr) <= 1) + continue; + + hsts_add(h, lineptr); + } + } while(!result && !eof); curlx_dyn_free(&buf); /* free the line buffer */ curlx_fclose(fp); } diff --git a/lib/netrc.c b/lib/netrc.c index f06dff8ed5..1309d30942 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -81,22 +81,27 @@ static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) curlx_dyn_init(&linebuf, MAX_NETRC_LINE); if(file) { + CURLcode result = CURLE_OK; + bool eof; ret = NETRC_OK; - while(Curl_get_line(&linebuf, file)) { - CURLcode result; - const char *line = curlx_dyn_ptr(&linebuf); - /* skip comments on load */ - curlx_str_passblanks(&line); - if(*line == '#') - continue; - result = curlx_dyn_add(filebuf, line); + do { + const char *line; + result = Curl_get_line(&linebuf, file, &eof); + if(!result) { + line = curlx_dyn_ptr(&linebuf); + /* skip comments on load */ + curlx_str_passblanks(&line); + if(*line == '#') + continue; + result = curlx_dyn_add(filebuf, line); + } if(result) { + curlx_dyn_free(filebuf); ret = curl2netrc(result); - goto done; + break; } - } + } while(!eof); } -done: curlx_dyn_free(&linebuf); if(file) curlx_fclose(file); diff --git a/tests/unit/unit3200.c b/tests/unit/unit3200.c index 5c3e4d14ad..15abba25db 100644 --- a/tests/unit/unit3200.c +++ b/tests/unit/unit3200.c @@ -76,12 +76,13 @@ static CURLcode test_unit3200(const char *arg) #endif size_t i; - int rc = 0; + CURLcode result = CURLE_OK; for(i = 0; i < CURL_ARRAYSIZE(filecontents); i++) { FILE *fp; struct dynbuf buf; size_t len = 4096; char *line; + bool eof; curlx_dyn_init(&buf, len); fp = curlx_fopen(arg, "wb"); @@ -95,63 +96,63 @@ static CURLcode test_unit3200(const char *arg) curl_mfprintf(stderr, "Test %zd...", i); switch(i) { case 0: - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE1\n", line), + fail_unless(!result && line && !strcmp("LINE1\n", line), "First line failed (1)"); - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE2 NEWLINE\n", line), + fail_unless(!result && line && !strcmp("LINE2 NEWLINE\n", line), "Second line failed (1)"); - rc = Curl_get_line(&buf, fp); - abort_unless(!curlx_dyn_len(&buf), "Missed EOF (1)"); + result = Curl_get_line(&buf, fp, &eof); + abort_unless(eof, "Missed EOF (1)"); break; case 1: - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE1\n", line), + fail_unless(!result && line && !strcmp("LINE1\n", line), "First line failed (2)"); - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE2 NONEWLINE\n", line), + fail_unless(!result && line && !strcmp("LINE2 NONEWLINE\n", line), "Second line failed (2)"); - rc = Curl_get_line(&buf, fp); - abort_unless(!curlx_dyn_len(&buf), "Missed EOF (2)"); + result = Curl_get_line(&buf, fp, &eof); + abort_unless(eof, "Missed EOF (2)"); break; case 2: - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE1\n", line), + fail_unless(!result && line && !strcmp("LINE1\n", line), "First line failed (3)"); - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); fail_unless(!curlx_dyn_len(&buf), "Did not detect max read on EOF (3)"); break; case 3: - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE1\n", line), + fail_unless(!result && line && !strcmp("LINE1\n", line), "First line failed (4)"); - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); fail_unless(!curlx_dyn_len(&buf), "Did not ignore partial on EOF (4)"); break; case 4: - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE1\n", line), + fail_unless(!result && line && !strcmp("LINE1\n", line), "First line failed (5)"); - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); fail_unless(!curlx_dyn_len(&buf), "Did not bail out on too long line"); break; case 5: - rc = Curl_get_line(&buf, fp); + result = Curl_get_line(&buf, fp, &eof); line = curlx_dyn_ptr(&buf); - fail_unless(rc && line && !strcmp("LINE1\x1aTEST\n", line), + fail_unless(!result && line && !strcmp("LINE1\x1aTEST\n", line), "Missed/Misinterpreted ^Z (6)"); - rc = Curl_get_line(&buf, fp); - abort_unless(!curlx_dyn_len(&buf), "Missed EOF (6)"); + result = Curl_get_line(&buf, fp, &eof); + abort_unless(eof, "Missed EOF (6)"); break; default: abort_unless(1, "Unknown case"); @@ -161,7 +162,7 @@ static CURLcode test_unit3200(const char *arg) curlx_fclose(fp); curl_mfprintf(stderr, "OK\n"); } - return (CURLcode)rc; + return result; #endif