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