From d175970f402a07706f7cf82b032be5037b2cebb5 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 16 May 2004 19:03:42 +0000 Subject: [PATCH] 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/ChangeLog | 30 ++++++++++++ lib/getline.c | 32 ++++-------- lib/getndelim2.c | 123 +++++++++++++++++++++++++++++------------------ lib/getndelim2.h | 29 +++++------ lib/getnline.c | 12 ++--- lib/getnline.h | 7 ++- 6 files changed, 139 insertions(+), 94 deletions(-) diff --git a/lib/ChangeLog b/lib/ChangeLog index 7ce0bf5dad..37f2703b67 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,33 @@ +2004-05-16 Derek Price + Paul Eggert + + 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 , , + 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 * nanosleep.c (suspended): Change its type from int to diff --git a/lib/getline.c b/lib/getline.c index 896b6bbd84..746d708878 100644 --- 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 #endif -/* Specification. */ #include "getline.h" -#include -#include - -/* Get ssize_t. */ -#include - -#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 diff --git a/lib/getndelim2.c b/lib/getndelim2.c index 2b6dc11571..c3c0566be7 100644 --- a/lib/getndelim2.c +++ b/lib/getndelim2.c @@ -24,82 +24,106 @@ # include #endif -/* Specification. */ #include "getndelim2.h" #include #include "unlocked-io.h" -/* Always add at least this many bytes when extending the buffer. */ +#include +#if HAVE_INTTYPES_H +# include +#endif +#if HAVE_STDINT_H +# include +#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; } @@ -110,14 +134,19 @@ getndelim2 (char **lineptr, size_t *linesize, size_t nmax, 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; } diff --git a/lib/getndelim2.h b/lib/getndelim2.h index d9bafe3b42..39f80cb557 100644 --- 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 #include - -/* Get ssize_t. */ #include -/* 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 */ diff --git a/lib/getnline.c b/lib/getnline.c index 7b858e37fa..9b2ce3ba0a 100644 --- 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); } diff --git a/lib/getnline.h b/lib/getnline.h index e455300ab9..fae1367caf 100644 --- 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,12 +19,11 @@ #ifndef GETNLINE_H #define GETNLINE_H 1 -#include #include - -/* Get ssize_t. */ #include +#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 -- 2.30.2