Blame gl/read-file.c

Packit 549fdc
/* read-file.c -- read file contents into a string
Packit 549fdc
   Copyright (C) 2006, 2009-2016 Free Software Foundation, Inc.
Packit 549fdc
   Written by Simon Josefsson and Bruno Haible.
Packit 549fdc
Packit 549fdc
   This program is free software; you can redistribute it and/or modify
Packit 549fdc
   it under the terms of the GNU Lesser General Public License as published by
Packit 549fdc
   the Free Software Foundation; either version 2.1, or (at your option)
Packit 549fdc
   any later version.
Packit 549fdc
Packit 549fdc
   This program is distributed in the hope that it will be useful,
Packit 549fdc
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 549fdc
   GNU Lesser General Public License for more details.
Packit 549fdc
Packit 549fdc
   You should have received a copy of the GNU Lesser General Public License
Packit 549fdc
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 549fdc
Packit 549fdc
#include <config.h>
Packit 549fdc
Packit 549fdc
#include "read-file.h"
Packit 549fdc
Packit 549fdc
/* Get fstat.  */
Packit 549fdc
#include <sys/stat.h>
Packit 549fdc
Packit 549fdc
/* Get ftello.  */
Packit 549fdc
#include <stdio.h>
Packit 549fdc
Packit 549fdc
/* Get SIZE_MAX.  */
Packit 549fdc
#include <stdint.h>
Packit 549fdc
Packit 549fdc
/* Get malloc, realloc, free. */
Packit 549fdc
#include <stdlib.h>
Packit 549fdc
Packit 549fdc
/* Get errno. */
Packit 549fdc
#include <errno.h>
Packit 549fdc
Packit 549fdc
/* Read a STREAM and return a newly allocated string with the content,
Packit 549fdc
   and set *LENGTH to the length of the string.  The string is
Packit 549fdc
   zero-terminated, but the terminating zero byte is not counted in
Packit 549fdc
   *LENGTH.  On errors, *LENGTH is undefined, errno preserves the
Packit 549fdc
   values set by system functions (if any), and NULL is returned.  */
Packit 549fdc
char *
Packit 549fdc
fread_file (FILE *stream, size_t *length)
Packit 549fdc
{
Packit 549fdc
  char *buf = NULL;
Packit 549fdc
  size_t alloc = BUFSIZ;
Packit 549fdc
Packit 549fdc
  /* For a regular file, allocate a buffer that has exactly the right
Packit 549fdc
     size.  This avoids the need to do dynamic reallocations later.  */
Packit 549fdc
  {
Packit 549fdc
    struct stat st;
Packit 549fdc
Packit 549fdc
    if (fstat (fileno (stream), &st) >= 0 && S_ISREG (st.st_mode))
Packit 549fdc
      {
Packit 549fdc
        off_t pos = ftello (stream);
Packit 549fdc
Packit 549fdc
        if (pos >= 0 && pos < st.st_size)
Packit 549fdc
          {
Packit 549fdc
            off_t alloc_off = st.st_size - pos;
Packit 549fdc
Packit 549fdc
            /* '1' below, accounts for the trailing NUL.  */
Packit 549fdc
            if (SIZE_MAX - 1 < alloc_off)
Packit 549fdc
              {
Packit 549fdc
                errno = ENOMEM;
Packit 549fdc
                return NULL;
Packit 549fdc
              }
Packit 549fdc
Packit 549fdc
            alloc = alloc_off + 1;
Packit 549fdc
          }
Packit 549fdc
      }
Packit 549fdc
  }
Packit 549fdc
Packit 549fdc
  if (!(buf = malloc (alloc)))
Packit 549fdc
    return NULL; /* errno is ENOMEM.  */
Packit 549fdc
Packit 549fdc
  {
Packit 549fdc
    size_t size = 0; /* number of bytes read so far */
Packit 549fdc
    int save_errno;
Packit 549fdc
Packit 549fdc
    for (;;)
Packit 549fdc
      {
Packit 549fdc
        /* This reads 1 more than the size of a regular file
Packit 549fdc
           so that we get eof immediately.  */
Packit 549fdc
        size_t requested = alloc - size;
Packit 549fdc
        size_t count = fread (buf + size, 1, requested, stream);
Packit 549fdc
        size += count;
Packit 549fdc
Packit 549fdc
        if (count != requested)
Packit 549fdc
          {
Packit 549fdc
            save_errno = errno;
Packit 549fdc
            if (ferror (stream))
Packit 549fdc
              break;
Packit 549fdc
Packit 549fdc
            /* Shrink the allocated memory if possible.  */
Packit 549fdc
            if (size < alloc - 1)
Packit 549fdc
              {
Packit 549fdc
                char *smaller_buf = realloc (buf, size + 1);
Packit 549fdc
                if (smaller_buf != NULL)
Packit 549fdc
                  buf = smaller_buf;
Packit 549fdc
              }
Packit 549fdc
Packit 549fdc
            buf[size] = '\0';
Packit 549fdc
            *length = size;
Packit 549fdc
            return buf;
Packit 549fdc
          }
Packit 549fdc
Packit 549fdc
        {
Packit 549fdc
          char *new_buf;
Packit 549fdc
Packit 549fdc
          if (alloc == SIZE_MAX)
Packit 549fdc
            {
Packit 549fdc
              save_errno = ENOMEM;
Packit 549fdc
              break;
Packit 549fdc
            }
Packit 549fdc
Packit 549fdc
          if (alloc < SIZE_MAX - alloc / 2)
Packit 549fdc
            alloc = alloc + alloc / 2;
Packit 549fdc
          else
Packit 549fdc
            alloc = SIZE_MAX;
Packit 549fdc
Packit 549fdc
          if (!(new_buf = realloc (buf, alloc)))
Packit 549fdc
            {
Packit 549fdc
              save_errno = errno;
Packit 549fdc
              break;
Packit 549fdc
            }
Packit 549fdc
Packit 549fdc
          buf = new_buf;
Packit 549fdc
        }
Packit 549fdc
      }
Packit 549fdc
Packit 549fdc
    free (buf);
Packit 549fdc
    errno = save_errno;
Packit 549fdc
    return NULL;
Packit 549fdc
  }
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
static char *
Packit 549fdc
internal_read_file (const char *filename, size_t *length, const char *mode)
Packit 549fdc
{
Packit 549fdc
  FILE *stream = fopen (filename, mode);
Packit 549fdc
  char *out;
Packit 549fdc
  int save_errno;
Packit 549fdc
Packit 549fdc
  if (!stream)
Packit 549fdc
    return NULL;
Packit 549fdc
Packit 549fdc
  out = fread_file (stream, length);
Packit 549fdc
Packit 549fdc
  save_errno = errno;
Packit 549fdc
Packit 549fdc
  if (fclose (stream) != 0)
Packit 549fdc
    {
Packit 549fdc
      if (out)
Packit 549fdc
        {
Packit 549fdc
          save_errno = errno;
Packit 549fdc
          free (out);
Packit 549fdc
        }
Packit 549fdc
      errno = save_errno;
Packit 549fdc
      return NULL;
Packit 549fdc
    }
Packit 549fdc
Packit 549fdc
  return out;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* Open and read the contents of FILENAME, and return a newly
Packit 549fdc
   allocated string with the content, and set *LENGTH to the length of
Packit 549fdc
   the string.  The string is zero-terminated, but the terminating
Packit 549fdc
   zero byte is not counted in *LENGTH.  On errors, *LENGTH is
Packit 549fdc
   undefined, errno preserves the values set by system functions (if
Packit 549fdc
   any), and NULL is returned.  */
Packit 549fdc
char *
Packit 549fdc
read_file (const char *filename, size_t *length)
Packit 549fdc
{
Packit 549fdc
  return internal_read_file (filename, length, "r");
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* Open (on non-POSIX systems, in binary mode) and read the contents
Packit 549fdc
   of FILENAME, and return a newly allocated string with the content,
Packit 549fdc
   and set LENGTH to the length of the string.  The string is
Packit 549fdc
   zero-terminated, but the terminating zero byte is not counted in
Packit 549fdc
   the LENGTH variable.  On errors, *LENGTH is undefined, errno
Packit 549fdc
   preserves the values set by system functions (if any), and NULL is
Packit 549fdc
   returned.  */
Packit 549fdc
char *
Packit 549fdc
read_binary_file (const char *filename, size_t *length)
Packit 549fdc
{
Packit 549fdc
  return internal_read_file (filename, length, "rb");
Packit 549fdc
}