diff options
author | Simon Josefsson <simon@josefsson.org> | 2010-10-14 15:52:47 +0200 |
---|---|---|
committer | Simon Josefsson <simon@josefsson.org> | 2010-10-14 15:52:47 +0200 |
commit | dbe767085787f5fdec183749a86c279a28676cc8 (patch) | |
tree | f618faa5de5488463a8715a410915cb0ddfec04a /lib/gl/read-file.c | |
parent | 9b3338ea218a58adb2f573f023718be1f0372da4 (diff) | |
download | gnutls-dbe767085787f5fdec183749a86c279a28676cc8.tar.gz |
Update gnulib files.
Diffstat (limited to 'lib/gl/read-file.c')
-rw-r--r-- | lib/gl/read-file.c | 142 |
1 files changed, 100 insertions, 42 deletions
diff --git a/lib/gl/read-file.c b/lib/gl/read-file.c index 3d27d7193c..38b26e47c8 100644 --- a/lib/gl/read-file.c +++ b/lib/gl/read-file.c @@ -20,7 +20,16 @@ #include "read-file.h" -/* Get realloc, free. */ +/* Get fstat. */ +#include <sys/stat.h> + +/* Get ftello. */ +#include <stdio.h> + +/* Get SIZE_MAX. */ +#include <stdint.h> + +/* Get malloc, realloc, free. */ #include <stdlib.h> /* Get errno. */ @@ -36,50 +45,99 @@ fread_file (FILE * stream, size_t * length) { char *buf = NULL; size_t alloc = 0; - size_t size = 0; - int save_errno; - - for (;;) - { - size_t count; - size_t requested; - - if (size + BUFSIZ + 1 > alloc) - { - char *new_buf; - - alloc += alloc / 2; - if (alloc < size + BUFSIZ + 1) - alloc = size + BUFSIZ + 1; - new_buf = realloc (buf, alloc); - if (!new_buf) - { - save_errno = errno; + /* For a regular file, allocate a buffer that has exactly the right + size. This avoids the need to do dynamic reallocations later. */ + { + struct stat st; + + if (fstat (fileno (stream), &st) >= 0 && S_ISREG (st.st_mode)) + { + off_t pos = ftello (stream); + + if (pos >= 0 && pos < st.st_size) + { + off_t alloc_off = st.st_size - pos; + + if (SIZE_MAX <= alloc_off) + { + errno = ENOMEM; + return NULL; + } + + alloc = alloc_off + 1; + + buf = malloc (alloc); + if (!buf) + /* errno is ENOMEM. */ + return NULL; + } + } + } + + { + size_t size = 0; /* number of bytes read so far */ + int save_errno; + + for (;;) + { + size_t count; + size_t requested; + + if (size + BUFSIZ + 1 > alloc) + { + char *new_buf; + size_t new_alloc = alloc + alloc / 2; + + /* Check against overflow. */ + if (new_alloc < alloc) + { + save_errno = ENOMEM; + break; + } + + alloc = new_alloc; + if (alloc < size + BUFSIZ + 1) + alloc = size + BUFSIZ + 1; + + new_buf = realloc (buf, alloc); + if (!new_buf) + { + save_errno = errno; + break; + } + + buf = new_buf; + } + + requested = alloc - size - 1; + count = fread (buf + size, 1, requested, stream); + size += count; + + if (count != requested) + { + save_errno = errno; + if (ferror (stream)) break; - } - buf = new_buf; - } - - requested = alloc - size - 1; - count = fread (buf + size, 1, requested, stream); - size += count; - - if (count != requested) - { - save_errno = errno; - if (ferror (stream)) - break; - buf[size] = '\0'; - *length = size; - return buf; - } - } - - free (buf); - errno = save_errno; - return NULL; + /* Shrink the allocated memory if possible. */ + if (size + 1 < alloc) + { + char *smaller_buf = realloc (buf, size + 1); + if (smaller_buf != NULL) + buf = smaller_buf; + } + + buf[size] = '\0'; + *length = size; + return buf; + } + } + + free (buf); + errno = save_errno; + return NULL; + } } static char * |