Blame gnulib/lib/getdelim.c

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