From b6ade08beedcfa4c01d642ee383618b22ab09b45 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 1 May 2008 16:49:21 +0200
Subject: [PATCH] Fix computation of newsize during reallocation.

---
 ChangeLog        |  5 +++++
 lib/getndelim2.c | 21 ++++++++++++++-------
 2 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 03e2df7405..f3b5bf9361 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2008-05-01  Bruno Haible  <bruno@clisp.org>
+
+	* lib/getndelim2.c (getndelim2): Fix newsize computation during
+	reallocation. Rename 'done' to 'found_delimiter'.
+
 2008-05-01  Jim Meyering  <meyering@redhat.com>
 
 	vc-list-files: accommodate /bin/sh like the one from Solaris 10
diff --git a/lib/getndelim2.c b/lib/getndelim2.c
index 04349ad7e9..9dcb9feb46 100644
--- a/lib/getndelim2.c
+++ b/lib/getndelim2.c
@@ -76,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)
     {
@@ -103,7 +103,8 @@ 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.
 	 Also nbytes_avail > 0 || size < nmax.  */
@@ -121,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;
 		}
 	    }
 	}
@@ -137,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;
 	}
 
@@ -145,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;
 
@@ -193,6 +199,7 @@ getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
       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.  */
-- 
2.30.2