and meaning of delim2 (now uses EOF, not 0, to indicate no delimiter).
+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
/* 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
# include <config.h>
#endif
-/* Specification. */
#include "getline.h"
-#include <stddef.h>
-#include <stdio.h>
-
-/* Get ssize_t. */
-#include <sys/types.h>
-
-#if defined __GNU_LIBRARY__ && HAVE_GETDELIM
-
-ssize_t
-getline (char **lineptr, size_t *linesize, FILE *stream)
-{
- return getdelim (lineptr, linesize, '\n', stream);
-}
-
-#else /* ! have getdelim */
+#if ! (defined __GNU_LIBRARY__ && HAVE_GETDELIM)
# include "getndelim2.h"
ssize_t
-getline (char **lineptr, size_t *linesize, FILE *stream)
+getdelim (char **lineptr, size_t *linesize, int delimiter, FILE *stream)
{
- return getndelim2 (lineptr, linesize, (size_t)(-1), stream, '\n', 0, 0);
+ return getndelim2 (lineptr, linesize, 0, GETNLINE_NO_LIMIT, delimiter, EOF,
+ stream);
}
+#endif
ssize_t
-getdelim (char **lineptr, size_t *linesize, int delimiter, FILE *stream)
+getline (char **lineptr, size_t *linesize, FILE *stream)
{
- return getndelim2 (lineptr, linesize, (size_t)(-1), stream, delimiter, 0, 0);
+ return getdelim (lineptr, linesize, '\n', stream);
}
-#endif
# 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. */
+ ssize_t bytes_stored = -1;
+ char *ptr = *lineptr;
+ size_t size = *linesize;
- if (!lineptr || !linesize || !nmax || !stream)
- return -1;
-
- 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;
-
- if (! (*linesize < newlinesize && newlinesize <= nmax))
- newlinesize = nmax;
-
- *linesize = newlinesize;
- nbytes_avail = *linesize + *lineptr - read_pos;
- p = realloc (*lineptr, *linesize);
- if (!p)
- return -1;
- *lineptr = p;
- read_pos = *linesize - nbytes_avail + *lineptr;
+ size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size;
+ char *newptr;
+
+ if (! (size < newsize && newsize <= nmax))
+ newsize = nmax;
+
+ if (GETNDELIM2_MAXIMUM < newsize - offset)
+ {
+ size_t newsizemax = offset + GETNDELIM2_MAXIMUM + 1;
+ if (size == newsizemax)
+ goto done;
+ newsize = newsizemax;
+ }
+
+ 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;
}
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;
}
/* 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
#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 */
/* 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
#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);
}
/* 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
#ifndef GETNLINE_H
#define GETNLINE_H 1
-#include <stddef.h>
#include <stdio.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
bytes of space. It is realloc'd as necessary. Reallocation is limited to