1/* getdelim.c --- Implementation of replacement getdelim function. 2 Copyright (C) 1994, 1996-1998, 2001, 2003, 2005-2012 Free Software 3 Foundation, Inc. 4 5 This program is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or (at 8 your option) any later version. 9 10 This program is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, see <http://www.gnu.org/licenses/>. */ 17 18/* Ported from glibc by Simon Josefsson. */ 19 20#include <config.h> 21 22/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc 23 optimizes away the lineptr == NULL || n == NULL || fp == NULL tests below. */ 24#define _GL_ARG_NONNULL(params) 25 26#include <stdio.h> 27 28#include <limits.h> 29#include <stdint.h> 30#include <stdlib.h> 31#include <errno.h> 32 33#ifndef SSIZE_MAX 34# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) 35#endif 36 37#if USE_UNLOCKED_IO 38# include "unlocked-io.h" 39# define getc_maybe_unlocked(fp) getc(fp) 40#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED 41# undef flockfile 42# undef funlockfile 43# define flockfile(x) ((void) 0) 44# define funlockfile(x) ((void) 0) 45# define getc_maybe_unlocked(fp) getc(fp) 46#else 47# define getc_maybe_unlocked(fp) getc_unlocked(fp) 48#endif 49 50/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and 51 NUL-terminate it). *LINEPTR is a pointer returned from malloc (or 52 NULL), pointing to *N characters of space. It is realloc'ed as 53 necessary. Returns the number of characters read (not including 54 the null terminator), or -1 on error or EOF. */ 55 56ssize_t 57getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp) 58{ 59 ssize_t result; 60 size_t cur_len = 0; 61 62 if (lineptr == NULL || n == NULL || fp == NULL) 63 { 64 errno = EINVAL; 65 return -1; 66 } 67 68 flockfile (fp); 69 70 if (*lineptr == NULL || *n == 0) 71 { 72 char *new_lineptr; 73 *n = 120; 74 new_lineptr = (char *) realloc (*lineptr, *n); 75 if (new_lineptr == NULL) 76 { 77 result = -1; 78 goto unlock_return; 79 } 80 *lineptr = new_lineptr; 81 } 82 83 for (;;) 84 { 85 int i; 86 87 i = getc_maybe_unlocked (fp); 88 if (i == EOF) 89 { 90 result = -1; 91 break; 92 } 93 94 /* Make enough space for len+1 (for final NUL) bytes. */ 95 if (cur_len + 1 >= *n) 96 { 97 size_t needed_max = 98 SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; 99 size_t needed = 2 * *n + 1; /* Be generous. */ 100 char *new_lineptr; 101 102 if (needed_max < needed) 103 needed = needed_max; 104 if (cur_len + 1 >= needed) 105 { 106 result = -1; 107 errno = EOVERFLOW; 108 goto unlock_return; 109 } 110 111 new_lineptr = (char *) realloc (*lineptr, needed); 112 if (new_lineptr == NULL) 113 { 114 result = -1; 115 goto unlock_return; 116 } 117 118 *lineptr = new_lineptr; 119 *n = needed; 120 } 121 122 (*lineptr)[cur_len] = i; 123 cur_len++; 124 125 if (i == delimiter) 126 break; 127 } 128 (*lineptr)[cur_len] = '\0'; 129 result = cur_len ? cur_len : result; 130 131 unlock_return: 132 funlockfile (fp); /* doesn't set errno */ 133 134 return result; 135} 136