Blame gl/read-file.c

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