changeset 5062:aa119e787246

getline cleanup. This changes the getndelim2 API: both order of arguments, and meaning of delim2 (now uses EOF, not 0, to indicate no delimiter).
author Paul Eggert <eggert@cs.ucla.edu>
date Sun, 16 May 2004 19:03:42 +0000
parents 849caac87bd5
children 1aecdc8063ba
files lib/ChangeLog lib/getline.c lib/getndelim2.c lib/getndelim2.h lib/getnline.c lib/getnline.h
diffstat 6 files changed, 140 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,33 @@
+2004-05-16  Derek Price  <derek@ximbiot.com>
+	    Paul Eggert  <eggert@cs.ucla.edu>
+
+	getline cleanup.  This changes the getndelim2 API: both order of
+	arguments, and meaning of delim2 (now uses EOF, not 0, to indicate
+	no delimiter).
+	
+	* lib/getline.c: Don't include stddef.h or stdio.h, since our
+	interface does that.
+	(getline): Always use getdelim, so that we don't have two
+	copies of this code.
+	* lib/getndelim2.c: Include <limits.h>, <inttypes.h>, <stdint.h>
+	if available.
+	(PTRDIFF_MAX, SIZE_MAX, SSIZE_MAX): Define if not defined.
+	(GETNDELIM2_MAXIMUM): New macro.
+	(getndelim2): Reorder arguments.  delim==EOF now means no delimiter,
+	instead of the old practice of delim2==0.  All callers changed.
+	Return -1 on overflow, instead of returning junk.
+	Do not set *linesize unless allocation succeeds.
+	* lib/getndelim2.h: Do not include stddef.h; no longer needed, now
+	that we include sys/types.h.
+	* lib/getnline.h: Likewise.
+	* lib/getndelim2.h (GETNLINE_NO_LIMIT): New macro.
+	(getndelim2): Reorder arguments.
+	* lib/getnline.c (getnline, getndelim):
+	Don't discard the NMAX argument.
+	(getnline): Invoke getndelim, to avoid code duplication.
+	* lib/getnline.h (GETNLINE_NO_LIMIT): New macro, used instead
+	of (size_t) -1 by callers of the getnline family.
+
 2004-05-13  Paul Eggert  <eggert@cs.ucla.edu>
 
 	* nanosleep.c (suspended): Change its type from int to
--- a/lib/getline.c
+++ b/lib/getline.c
@@ -1,7 +1,7 @@
 /* getline.c -- Replacement for GNU C library function getline
 
-   Copyright (C) 1993, 1996, 1997, 1998, 2000, 2003 Free Software
-   Foundation, Inc.
+   Copyright (C) 1993, 1996, 1997, 1998, 2000, 2003, 2004 Free
+   Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -23,36 +23,22 @@
 # include <config.h>
 #endif
 
-/* Specification.  */
 #include "getline.h"
 
-#include <stddef.h>
-#include <stdio.h>
+#if ! (defined __GNU_LIBRARY__ && HAVE_GETDELIM)
+
+# include "getndelim2.h"
 
-/* Get ssize_t.  */
-#include <sys/types.h>
-
-#if defined __GNU_LIBRARY__ && HAVE_GETDELIM
+ssize_t
+getdelim (char **lineptr, size_t *linesize, int delimiter, FILE *stream)
+{
+  return getndelim2 (lineptr, linesize, 0, GETNLINE_NO_LIMIT, delimiter, EOF,
+                     stream);
+}
+#endif
 
 ssize_t
 getline (char **lineptr, size_t *linesize, FILE *stream)
 {
   return getdelim (lineptr, linesize, '\n', stream);
 }
-
-#else /* ! have getdelim */
-
-# include "getndelim2.h"
-
-ssize_t
-getline (char **lineptr, size_t *linesize, FILE *stream)
-{
-  return getndelim2 (lineptr, linesize, (size_t)(-1), stream, '\n', 0, 0);
-}
-
-ssize_t
-getdelim (char **lineptr, size_t *linesize, int delimiter, FILE *stream)
-{
-  return getndelim2 (lineptr, linesize, (size_t)(-1), stream, delimiter, 0, 0);
-}
-#endif
--- a/lib/getndelim2.c
+++ b/lib/getndelim2.c
@@ -24,82 +24,106 @@
 # include <config.h>
 #endif
 
-/* Specification.  */
 #include "getndelim2.h"
 
 #include <stdlib.h>
 
 #include "unlocked-io.h"
 
-/* Always add at least this many bytes when extending the buffer.  */
+#include <limits.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX ((ptrdiff_t) (SIZE_MAX / 2))
+#endif
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+#ifndef SSIZE_MAX
+# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
+#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).  */
+#define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
+
+/* Try to add at least this many bytes when extending the buffer.
+   MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM.  */
 #define MIN_CHUNK 64
 
 ssize_t
-getndelim2 (char **lineptr, size_t *linesize, size_t nmax,
-	    FILE *stream, int delim1, int delim2, size_t offset)
+getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax,
+            int delim1, int delim2, FILE *stream)
 {
-  size_t nbytes_avail;		/* Allocated but unused chars in *LINEPTR.  */
+  size_t nbytes_avail;		/* Allocated but unused bytes in *LINEPTR.  */
   char *read_pos;		/* Where we're reading into *LINEPTR. */
-
-  if (!lineptr || !linesize || !nmax || !stream)
-    return -1;
+  ssize_t bytes_stored = -1;
+  char *ptr = *lineptr;
+  size_t size = *linesize;
 
-  if (!*lineptr)
+  if (!ptr)
     {
-      size_t newlinesize = MIN_CHUNK;
-
-      if (newlinesize > nmax)
-	newlinesize = nmax;
-
-      *linesize = newlinesize;
-      *lineptr = malloc (*linesize);
-      if (!*lineptr)
+      size = nmax < MIN_CHUNK ? nmax : MIN_CHUNK;
+      ptr = malloc (size);
+      if (!ptr)
 	return -1;
     }
 
-  if (*linesize < offset)
-    return -1;
+  if (size < offset)
+    goto done;
 
-  nbytes_avail = *linesize - offset;
-  read_pos = *lineptr + offset;
+  nbytes_avail = size - offset;
+  read_pos = ptr + offset;
 
-  if (nbytes_avail == 0 && *linesize >= nmax)
-    return -1;
+  if (nbytes_avail == 0 && nmax <= size)
+    goto done;
 
   for (;;)
     {
-      /* Here always *lineptr + *linesize == read_pos + nbytes_avail.  */
+      /* Here always ptr + size == read_pos + nbytes_avail.  */
 
-      register int c;
+      int c;
 
-      /* We always want at least one char left in the buffer, since we
-	 always (unless we get an error while reading the first char)
+      /* We always want at least one byte left in the buffer, since we
+	 always (unless we get an error while reading the first byte)
 	 NUL-terminate the line buffer.  */
 
-      if (nbytes_avail < 2 && *linesize < nmax)
+      if (nbytes_avail < 2 && size < nmax)
 	{
-	  size_t newlinesize =
-	    (*linesize > MIN_CHUNK ? 2 * *linesize : *linesize + MIN_CHUNK);
-	  char *p;
+	  size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size;
+	  char *newptr;
+
+	  if (! (size < newsize && newsize <= nmax))
+	    newsize = nmax;
 
-	  if (! (*linesize < newlinesize && newlinesize <= nmax))
-	    newlinesize = nmax;
+	  if (GETNDELIM2_MAXIMUM < newsize - offset)
+	    {
+	      size_t newsizemax = offset + GETNDELIM2_MAXIMUM + 1;
+	      if (size == newsizemax)
+		goto done;
+	      newsize = newsizemax;
+	    }
 
-	  *linesize = newlinesize;
-	  nbytes_avail = *linesize + *lineptr - read_pos;
-	  p = realloc (*lineptr, *linesize);
-	  if (!p)
-	    return -1;
-	  *lineptr = p;
-	  read_pos = *linesize - nbytes_avail + *lineptr;
+	  nbytes_avail = newsize - (read_pos - ptr);
+	  newptr = realloc (ptr, newsize);
+	  if (!newptr)
+	    goto done;
+	  ptr = newptr;
+	  size = newsize;
+	  read_pos = size - nbytes_avail + ptr;
 	}
 
       c = getc (stream);
       if (c == EOF)
 	{
 	  /* Return partial line, if any.  */
-	  if (read_pos == *lineptr)
-	    return -1;
+	  if (read_pos == ptr)
+	    goto done;
 	  else
 	    break;
 	}
@@ -110,14 +134,19 @@
 	  nbytes_avail--;
 	}
 
-      if (c == delim1 || (delim2 && c == delim2))
+      if (c == delim1 || c == delim2)
 	/* Return the line.  */
 	break;
     }
 
-  /* Done - NUL terminate and return the number of chars read.
+  /* Done - NUL terminate and return the number of bytes read.
      At this point we know that nbytes_avail >= 1.  */
   *read_pos = '\0';
 
-  return read_pos - (*lineptr + offset);
+  bytes_stored = read_pos - (ptr + offset);
+
+ done:
+  *lineptr = ptr;
+  *linesize = size;
+  return bytes_stored;
 }
--- a/lib/getndelim2.h
+++ b/lib/getndelim2.h
@@ -1,7 +1,7 @@
 /* getndelim2 - Read a line from a stream, stopping at one of 2 delimiters,
    with bounded memory allocation.
 
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -20,23 +20,24 @@
 #ifndef GETNDELIM2_H
 #define GETNDELIM2_H 1
 
-#include <stddef.h>
 #include <stdio.h>
-
-/* Get ssize_t.  */
 #include <sys/types.h>
 
-/* Read up to (and including) a delimiter DELIM1 from STREAM into *LINEPTR
-   + OFFSET (and NUL-terminate it).  If DELIM2 is non-zero, then read up
-   and including the first occurrence of DELIM1 or DELIM2.  *LINEPTR is
-   a pointer returned from malloc (or NULL), pointing to *LINESIZE bytes of
-   space.  It is realloc'd as necessary.  Reallocation is limited to
-   NMAX bytes; if the line is longer than that, the extra bytes are read but
-   thrown away.
+#define GETNLINE_NO_LIMIT ((size_t) -1)
+
+/* Read into a buffer *LINEPTR returned from malloc (or NULL),
+   pointing to *LINESIZE bytes of space.  Store the input bytes
+   starting at *LINEPTR + OFFSET, and null-terminate them.  Reallocate
+   the buffer as necessary, but if NMAX is not GETNLINE_NO_LIMIT
+   then do not allocate more than NMAX bytes; if the line is longer
+   than that, read and discard the extra bytes.  Stop reading after
+   after the first occurrence of DELIM1 or DELIM2, whichever comes
+   first; a delimiter equal to EOF stands for no delimiter.  Read the
+   input bytes from STREAM.
    Return the number of bytes read and stored at *LINEPTR + OFFSET (not
    including the NUL terminator), or -1 on error or EOF.  */
-extern ssize_t getndelim2 (char **lineptr, size_t *linesize, size_t nmax,
-			   FILE *stream, int delim1, int delim2,
-			   size_t offset);
+extern ssize_t getndelim2 (char **lineptr, size_t *linesize, size_t offset,
+                           size_t nmax, int delim1, int delim2,
+                           FILE *stream);
 
 #endif /* GETNDELIM2_H */
--- a/lib/getnline.c
+++ b/lib/getnline.c
@@ -1,6 +1,6 @@
 /* getnline - Read a line from a stream, with bounded memory allocation.
 
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -26,14 +26,14 @@
 #include "getndelim2.h"
 
 ssize_t
-getnline (char **lineptr, size_t *linesize, size_t nmax, FILE *stream)
+getndelim (char **lineptr, size_t *linesize, size_t nmax,
+	   int delimiter, FILE *stream)
 {
-  return getndelim2 (lineptr, linesize, (size_t)(-1), stream, '\n', 0, 0);
+  return getndelim2 (lineptr, linesize, 0, nmax, delimiter, EOF, stream);
 }
 
 ssize_t
-getndelim (char **lineptr, size_t *linesize, size_t nmax,
-	   int delimiter, FILE *stream)
+getnline (char **lineptr, size_t *linesize, size_t nmax, FILE *stream)
 {
-  return getndelim2 (lineptr, linesize, (size_t)(-1), stream, delimiter, 0, 0);
+  return getndelim (lineptr, linesize, nmax, '\n', stream);
 }
--- a/lib/getnline.h
+++ b/lib/getnline.h
@@ -1,6 +1,6 @@
 /* getnline - Read a line from a stream, with bounded memory allocation.
 
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -19,11 +19,10 @@
 #ifndef GETNLINE_H
 #define GETNLINE_H 1
 
-#include <stddef.h>
 #include <stdio.h>
+#include <sys/types.h>
 
-/* Get ssize_t.  */
-#include <sys/types.h>
+#define GETNLINE_NO_LIMIT ((size_t) -1)
 
 /* Read a line, up to the next newline, from STREAM, and store it in *LINEPTR.
    *LINEPTR is a pointer returned from malloc (or NULL), pointing to *LINESIZE