fix test-poll compilation failure on Cygwin
[gnulib.git] / lib / getndelim2.c
index be34e40..9dcb9fe 100644 (file)
 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
 #endif
 
+/* Use this to suppress gcc's `...may be used before initialized' warnings. */
+#ifdef lint
+# define IF_LINT(Code) Code
+#else
+# define IF_LINT(Code) /* empty */
+#endif
+
 /* The maximum value that getndelim2 can return without suffering from
    overflow problems, either internally (because of pointer
    subtraction overflow) or due to the API (because of ssize_t).  */
@@ -69,7 +76,7 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
   ssize_t bytes_stored = -1;
   char *ptr = *lineptr;
   size_t size = *linesize;
-  bool done = false;
+  bool found_delimiter;
 
   if (!ptr)
     {
@@ -96,11 +103,13 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
 
   flockfile (stream);
 
-  while (!done)
+  found_delimiter = false;
+  do
     {
-      /* Here always ptr + size == read_pos + nbytes_avail.  */
+      /* Here always ptr + size == read_pos + nbytes_avail.
+        Also nbytes_avail > 0 || size < nmax.  */
 
-      int c;
+      int c IF_LINT (= 0);
       const char *buffer;
       size_t buffer_len;
 
@@ -113,7 +122,7 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
              if (end)
                {
                  buffer_len = end - buffer + 1;
-                 done = true;
+                 found_delimiter = true;
                }
            }
        }
@@ -129,7 +138,7 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
                break;
            }
          if (c == delim1 || c == delim2)
-           done = true;
+           found_delimiter = true;
          buffer_len = 1;
        }
 
@@ -137,13 +146,18 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
         always (unless we get an error while reading the first byte)
         NUL-terminate the line buffer.  */
 
-      if (nbytes_avail < 1 + buffer_len && size < nmax)
+      if (nbytes_avail < buffer_len + 1 && size < nmax)
        {
+         /* Grow size proportionally, not linearly, to avoid O(n^2)
+            running time.  */
          size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size;
          char *newptr;
 
-         if (newsize < buffer_len)
-           newsize = buffer_len + size;
+         /* Increase newsize so that it becomes
+            >= (read_pos - ptr) + buffer_len.  */
+         if (newsize - (read_pos - ptr) < buffer_len + 1)
+           newsize = (read_pos - ptr) + buffer_len + 1;
+         /* Respect nmax.  This handles possible integer overflow.  */
          if (! (size < newsize && newsize <= nmax))
            newsize = nmax;
 
@@ -164,6 +178,9 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
          read_pos = size - nbytes_avail + ptr;
        }
 
+      /* Here, if size < nmax, nbytes_avail >= buffer_len + 1.
+        If size == nmax, nbytes_avail > 0.  */
+
       if (1 < nbytes_avail)
        {
          size_t copy_len = nbytes_avail - 1;
@@ -176,9 +193,13 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
          read_pos += copy_len;
          nbytes_avail -= copy_len;
        }
+
+      /* Here still nbytes_avail > 0.  */
+
       if (buffer && freadseek (stream, buffer_len))
        goto unlock_done;
     }
+  while (!found_delimiter);
 
   /* Done - NUL terminate and return the number of bytes read.
      At this point we know that nbytes_avail >= 1.  */