@@ -37,14 +37,14 @@
null terminator), or -1 on error or EOF. */
_IO_ssize_t
-_IO_getdelim (lineptr, n, delimiter, fp)
+_IO_getdelim_append (lineptr, n, delimiter, fp, cur_len)
char **lineptr;
_IO_size_t *n;
int delimiter;
_IO_FILE *fp;
+ _IO_size_t *cur_len;
{
_IO_ssize_t result = 0;
- _IO_size_t cur_len = 0;
_IO_ssize_t len;
if (lineptr == NULL || n == NULL)
@@ -89,7 +89,7 @@ _IO_getdelim (lineptr, n, delimiter, fp)
t = (char *) memchr ((void *) fp->_IO_read_ptr, delimiter, len);
if (t != NULL)
len = (t - fp->_IO_read_ptr) + 1;
- if (__glibc_unlikely (len >= SIZE_MAX - cur_len) ||
+ if (__glibc_unlikely (len >= SIZE_MAX - *cur_len) ||
__glibc_unlikely (len >= SSIZE_MAX - result))
{
__set_errno (EOVERFLOW);
@@ -97,7 +97,7 @@ _IO_getdelim (lineptr, n, delimiter, fp)
goto unlock_return;
}
/* Make enough space for len+1 (for final NUL) bytes. */
- needed = cur_len + len + 1;
+ needed = *cur_len + len + 1;
if (needed > *n)
{
char *new_lineptr;
@@ -113,15 +113,15 @@ _IO_getdelim (lineptr, n, delimiter, fp)
*lineptr = new_lineptr;
*n = needed;
}
- memcpy (*lineptr + cur_len, (void *) fp->_IO_read_ptr, len);
+ memcpy (*lineptr + *cur_len, (void *) fp->_IO_read_ptr, len);
fp->_IO_read_ptr += len;
- cur_len += len;
+ *cur_len += len;
result += len;
if (t != NULL || __underflow (fp) == EOF)
break;
len = fp->_IO_read_end - fp->_IO_read_ptr;
}
- (*lineptr)[cur_len] = '\0';
+ (*lineptr)[*cur_len] = '\0';
unlock_return:
_IO_release_lock (fp);
@@ -129,6 +129,22 @@ unlock_return:
}
#ifdef weak_alias
+weak_alias (_IO_getdelim_append, __getdelim_append)
+weak_alias (_IO_getdelim_append, getdelim_append)
+#endif
+
+_IO_ssize_t
+_IO_getdelim (lineptr, n, delimiter, fp)
+ char **lineptr;
+ _IO_size_t *n;
+ int delimiter;
+ _IO_FILE *fp;
+{
+ _IO_size_t offset = 0;
+ return _IO_getdelim_append (lineptr, n, delimiter, fp, &offset);
+}
+
+#ifdef weak_alias
weak_alias (_IO_getdelim, __getdelim)
weak_alias (_IO_getdelim, getdelim)
#endif
@@ -688,6 +688,7 @@ extern _IO_size_t _IO_getline_info (_IO_FILE *,char *, _IO_size_t,
int, int, int *);
libc_hidden_proto (_IO_getline_info)
extern _IO_ssize_t _IO_getdelim (char **, _IO_size_t *, int, _IO_FILE *);
+extern _IO_ssize_t _IO_getdelim_append (char **, _IO_size_t *, int, _IO_FILE *, _IO_size_t *);
extern _IO_size_t _IO_getwline (_IO_FILE *,wchar_t *, _IO_size_t, wint_t, int);
extern _IO_size_t _IO_getwline_info (_IO_FILE *,wchar_t *, _IO_size_t,
wint_t, int, wint_t *);
If getdelim fails (e.g. due to ENOMEM), the contents which have already been read from the stream and copied to the output buffer are effectively lost: the bytes exist in the output buffer and *n faithfully reflects the allocated size of that, but the caller has no way of knowing how many actually came from the stream, and how many might be random malloc/realloc junk. This means that there is no way for the application to try to free some memory and retry the getdelim call or falling back to some other method (e.g. a slow getc loop). One way to solve this is to introduce getdelim_append, which has an extra in/out parameter allowing the caller to indicate the initial offset in the buffer to start writing at. getdelim_append updates this parameter every time content is copied to the output buffer. There are other use cases apart from allowing the application to try to recover from an error. For example, one can imagine reading a file format where a set of header lines is delimited by a blank line. Reading the entire header can then be done without maintaining both a line buffer and a final buffer, copying from one to the other: char *buf = NULL; size_t cap = 0, len = 0, old_len; ssize_t ret; do { old_len = len; ret = getdelim_append(&buf, &cap, '\n', f, &len); } while (ret > 0 && len-old_len > 1); (this could probably just be ret > 1, but in more complicated situations one could use old_len with ret and/or len to inspect the last record read). Signed-off-by: Rasmus Villemoes <rv@rasmusvillemoes.dk> --- libio/iogetdelim.c | 30 +++++++++++++++++++++++------- libio/libioP.h | 1 + 2 files changed, 24 insertions(+), 7 deletions(-)