X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fread-file.c;h=3bf9b846326e1cdb9f9268c4bec810d47131ad34;hb=07809063056aef043ba738ab8229042f1ffc9659;hp=27241b450483bffb08ae9e69eaaf37903b439e49;hpb=3e53db21df4287b43a46ad27fc2fac56b9194ffc;p=gnulib.git diff --git a/lib/read-file.c b/lib/read-file.c index 27241b450..3bf9b8463 100644 --- a/lib/read-file.c +++ b/lib/read-file.c @@ -1,5 +1,5 @@ /* read-file.c -- read file contents into a string - Copyright (C) 2006, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 2006, 2009-2011 Free Software Foundation, Inc. Written by Simon Josefsson and Bruno Haible. This program is free software; you can redistribute it and/or modify @@ -39,12 +39,12 @@ and set *LENGTH to the length of the string. The string is zero-terminated, but the terminating zero byte is not counted in *LENGTH. On errors, *LENGTH is undefined, errno preserves the - values set by system functions (if any), and NULL is returned. */ + values set by system functions (if any), and NULL is returned. */ char * -fread_file (FILE * stream, size_t * length) +fread_file (FILE *stream, size_t *length) { char *buf = NULL; - size_t alloc = 0; + size_t alloc = BUFSIZ; /* For a regular file, allocate a buffer that has exactly the right size. This avoids the need to do dynamic reallocations later. */ @@ -59,59 +59,31 @@ fread_file (FILE * stream, size_t * length) { off_t alloc_off = st.st_size - pos; - if (SIZE_MAX <= alloc_off) + /* '1' below, accounts for the trailing NUL. */ + if (SIZE_MAX - 1 < alloc_off) { errno = ENOMEM; return NULL; } alloc = alloc_off + 1; - - buf = malloc (alloc); - if (!buf) - /* errno is ENOMEM. */ - return NULL; } } } + if (!(buf = malloc (alloc))) + return NULL; /* errno is ENOMEM. */ + { 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); + /* This reads 1 more than the size of a regular file + so that we get eof immediately. */ + size_t requested = alloc - size; + size_t count = fread (buf + size, 1, requested, stream); size += count; if (count != requested) @@ -119,10 +91,42 @@ fread_file (FILE * stream, size_t * length) save_errno = errno; if (ferror (stream)) break; + + /* Shrink the allocated memory if possible. */ + if (size < alloc - 1) + { + char *smaller_buf = realloc (buf, size + 1); + if (smaller_buf != NULL) + buf = smaller_buf; + } + buf[size] = '\0'; *length = size; return buf; } + + { + char *new_buf; + + if (alloc == SIZE_MAX) + { + save_errno = ENOMEM; + break; + } + + if (alloc < SIZE_MAX - alloc / 2) + alloc = alloc + alloc / 2; + else + alloc = SIZE_MAX; + + if (!(new_buf = realloc (buf, alloc))) + { + save_errno = errno; + break; + } + + buf = new_buf; + } } free (buf); @@ -132,7 +136,7 @@ fread_file (FILE * stream, size_t * length) } static char * -internal_read_file (const char *filename, size_t * length, const char *mode) +internal_read_file (const char *filename, size_t *length, const char *mode) { FILE *stream = fopen (filename, mode); char *out; @@ -166,7 +170,7 @@ internal_read_file (const char *filename, size_t * length, const char *mode) undefined, errno preserves the values set by system functions (if any), and NULL is returned. */ char * -read_file (const char *filename, size_t * length) +read_file (const char *filename, size_t *length) { return internal_read_file (filename, length, "r"); } @@ -179,7 +183,7 @@ read_file (const char *filename, size_t * length) preserves the values set by system functions (if any), and NULL is returned. */ char * -read_binary_file (const char *filename, size_t * length) +read_binary_file (const char *filename, size_t *length) { return internal_read_file (filename, length, "rb"); }