]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
curl_get_line: enhance the API
authorDaniel Stenberg <daniel@haxx.se>
Sun, 19 Oct 2025 11:09:42 +0000 (13:09 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 19 Oct 2025 14:25:11 +0000 (16:25 +0200)
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

lib/altsvc.c
lib/cookie.c
lib/curl_get_line.c
lib/curl_get_line.h
lib/hsts.c
lib/netrc.c
tests/unit/unit3200.c

index 449bea8528dc595b75ed619367ae81a5d1a2087c..d9933f2298020f6b190f3462a855700987618bd8 100644 (file)
@@ -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);
   }
index 59a841a303c78d96d7de43791811238c473c17f6..98c13e621868030763bbb3448fcd057b32dbcb3a 100644 (file)
@@ -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 */
 
       /*
index 4b1c6c3e0970ce7b749592ac79dcea1134fda246..b1b40212213d079f9ec333c8b47e58784a063966 100644 (file)
 /* 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 */
index d4877123f26169dbb9a0438a071b8e8ee4279926..176d5f7a30e302b95f64260a1620d9fdc78eb66e 100644 (file)
@@ -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 */
index 28989764b90ff6e36c2b3faa180710f1b7681754..4e41155f300632615a9c6b9893ce106779552567 100644 (file)
@@ -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);
   }
index f06dff8ed515e0afbef0eda64d23c520019cd913..1309d309423733d91c9edbea5d2347d44e33604f 100644 (file)
@@ -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);
index 5c3e4d14ad476303921a59b81a4ca22f0cd59463..15abba25db79fdb69f8775f04463822b18d925e9 100644 (file)
@@ -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