Blame gl/getdelim.c

Packit 549fdc
/* getdelim.c --- Implementation of replacement getdelim function.
Packit 549fdc
   Copyright (C) 1994, 1996-1998, 2001, 2003, 2005-2016 Free Software
Packit 549fdc
   Foundation, Inc.
Packit 549fdc
Packit 549fdc
   This program is free software; you can redistribute it and/or
Packit 549fdc
   modify it under the terms of the GNU Lesser General Public License as
Packit 549fdc
   published by the Free Software Foundation; either version 2.1, or (at
Packit 549fdc
   your option) any later version.
Packit 549fdc
Packit 549fdc
   This program is distributed in the hope that it will be useful, but
Packit 549fdc
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 549fdc
   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
/* Ported from glibc by Simon Josefsson. */
Packit 549fdc
Packit 549fdc
/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
Packit 549fdc
   optimizes away the lineptr == NULL || n == NULL || fp == NULL tests below.  */
Packit 549fdc
#define _GL_ARG_NONNULL(params)
Packit 549fdc
Packit 549fdc
#include <config.h>
Packit 549fdc
Packit 549fdc
#include <stdio.h>
Packit 549fdc
Packit 549fdc
#include <limits.h>
Packit 549fdc
#include <stdint.h>
Packit 549fdc
#include <stdlib.h>
Packit 549fdc
#include <errno.h>
Packit 549fdc
Packit 549fdc
#ifndef SSIZE_MAX
Packit 549fdc
# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
Packit 549fdc
#endif
Packit 549fdc
Packit 549fdc
#if USE_UNLOCKED_IO
Packit 549fdc
# include "unlocked-io.h"
Packit 549fdc
# define getc_maybe_unlocked(fp)        getc(fp)
Packit 549fdc
#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED
Packit 549fdc
# undef flockfile
Packit 549fdc
# undef funlockfile
Packit 549fdc
# define flockfile(x) ((void) 0)
Packit 549fdc
# define funlockfile(x) ((void) 0)
Packit 549fdc
# define getc_maybe_unlocked(fp)        getc(fp)
Packit 549fdc
#else
Packit 549fdc
# define getc_maybe_unlocked(fp)        getc_unlocked(fp)
Packit 549fdc
#endif
Packit 549fdc
Packit 549fdc
static void
Packit 549fdc
alloc_failed (void)
Packit 549fdc
{
Packit 549fdc
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit 549fdc
  /* Avoid errno problem without using the realloc module; see:
Packit 549fdc
     http://lists.gnu.org/archive/html/bug-gnulib/2016-08/msg00025.html  */
Packit 549fdc
  errno = ENOMEM;
Packit 549fdc
#endif
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
Packit 549fdc
   NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
Packit 549fdc
   NULL), pointing to *N characters of space.  It is realloc'ed as
Packit 549fdc
   necessary.  Returns the number of characters read (not including
Packit 549fdc
   the null terminator), or -1 on error or EOF.  */
Packit 549fdc
Packit 549fdc
ssize_t
Packit 549fdc
getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
Packit 549fdc
{
Packit 549fdc
  ssize_t result;
Packit 549fdc
  size_t cur_len = 0;
Packit 549fdc
Packit 549fdc
  if (lineptr == NULL || n == NULL || fp == NULL)
Packit 549fdc
    {
Packit 549fdc
      errno = EINVAL;
Packit 549fdc
      return -1;
Packit 549fdc
    }
Packit 549fdc
Packit 549fdc
  flockfile (fp);
Packit 549fdc
Packit 549fdc
  if (*lineptr == NULL || *n == 0)
Packit 549fdc
    {
Packit 549fdc
      char *new_lineptr;
Packit 549fdc
      *n = 120;
Packit 549fdc
      new_lineptr = (char *) realloc (*lineptr, *n);
Packit 549fdc
      if (new_lineptr == NULL)
Packit 549fdc
        {
Packit 549fdc
          alloc_failed ();
Packit 549fdc
          result = -1;
Packit 549fdc
          goto unlock_return;
Packit 549fdc
        }
Packit 549fdc
      *lineptr = new_lineptr;
Packit 549fdc
    }
Packit 549fdc
Packit 549fdc
  for (;;)
Packit 549fdc
    {
Packit 549fdc
      int i;
Packit 549fdc
Packit 549fdc
      i = getc_maybe_unlocked (fp);
Packit 549fdc
      if (i == EOF)
Packit 549fdc
        {
Packit 549fdc
          result = -1;
Packit 549fdc
          break;
Packit 549fdc
        }
Packit 549fdc
Packit 549fdc
      /* Make enough space for len+1 (for final NUL) bytes.  */
Packit 549fdc
      if (cur_len + 1 >= *n)
Packit 549fdc
        {
Packit 549fdc
          size_t needed_max =
Packit 549fdc
            SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
Packit 549fdc
          size_t needed = 2 * *n + 1;   /* Be generous. */
Packit 549fdc
          char *new_lineptr;
Packit 549fdc
Packit 549fdc
          if (needed_max < needed)
Packit 549fdc
            needed = needed_max;
Packit 549fdc
          if (cur_len + 1 >= needed)
Packit 549fdc
            {
Packit 549fdc
              result = -1;
Packit 549fdc
              errno = EOVERFLOW;
Packit 549fdc
              goto unlock_return;
Packit 549fdc
            }
Packit 549fdc
Packit 549fdc
          new_lineptr = (char *) realloc (*lineptr, needed);
Packit 549fdc
          if (new_lineptr == NULL)
Packit 549fdc
            {
Packit 549fdc
              alloc_failed ();
Packit 549fdc
              result = -1;
Packit 549fdc
              goto unlock_return;
Packit 549fdc
            }
Packit 549fdc
Packit 549fdc
          *lineptr = new_lineptr;
Packit 549fdc
          *n = needed;
Packit 549fdc
        }
Packit 549fdc
Packit 549fdc
      (*lineptr)[cur_len] = i;
Packit 549fdc
      cur_len++;
Packit 549fdc
Packit 549fdc
      if (i == delimiter)
Packit 549fdc
        break;
Packit 549fdc
    }
Packit 549fdc
  (*lineptr)[cur_len] = '\0';
Packit 549fdc
  result = cur_len ? cur_len : result;
Packit 549fdc
Packit 549fdc
 unlock_return:
Packit 549fdc
  funlockfile (fp); /* doesn't set errno */
Packit 549fdc
Packit 549fdc
  return result;
Packit 549fdc
}