1 1.1 christos /* getndelim2 - Read a line from a stream, stopping at one of 2 delimiters, 2 1.1 christos with bounded memory allocation. 3 1.1 christos 4 1.1 christos Copyright (C) 1993, 1996, 1997, 1998, 2000, 2003, 2004 Free Software 5 1.1 christos Foundation, Inc. 6 1.1 christos 7 1.1 christos This program is free software; you can redistribute it and/or modify 8 1.1 christos it under the terms of the GNU General Public License as published by 9 1.1 christos the Free Software Foundation; either version 2, or (at your option) 10 1.1 christos any later version. 11 1.1 christos 12 1.1 christos This program is distributed in the hope that it will be useful, 13 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 14 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 1.1 christos GNU General Public License for more details. 16 1.1 christos 17 1.1 christos You should have received a copy of the GNU General Public License 18 1.1 christos along with this program; if not, write to the Free Software Foundation, 19 1.1 christos Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 20 1.2 christos #include <sys/cdefs.h> 21 1.2 christos __RCSID("$NetBSD: getndelim2.c,v 1.2 2016/05/17 14:00:09 christos Exp $"); 22 1.2 christos 23 1.1 christos 24 1.1 christos /* Originally written by Jan Brittenson, bson (at) gnu.ai.mit.edu. */ 25 1.1 christos 26 1.1 christos #ifdef HAVE_CONFIG_H 27 1.1 christos # include <config.h> 28 1.1 christos #endif 29 1.1 christos 30 1.1 christos #include "getndelim2.h" 31 1.1 christos 32 1.1 christos #include <stdlib.h> 33 1.1 christos #include <stddef.h> 34 1.1 christos 35 1.1 christos #if USE_UNLOCKED_IO 36 1.1 christos # include "unlocked-io.h" 37 1.1 christos #endif 38 1.1 christos 39 1.1 christos #include <limits.h> 40 1.1 christos #if HAVE_INTTYPES_H 41 1.1 christos # include <inttypes.h> 42 1.1 christos #endif 43 1.1 christos #if HAVE_STDINT_H 44 1.1 christos # include <stdint.h> 45 1.1 christos #endif 46 1.1 christos #ifndef PTRDIFF_MAX 47 1.1 christos # define PTRDIFF_MAX ((ptrdiff_t) (SIZE_MAX / 2)) 48 1.1 christos #endif 49 1.1 christos #ifndef SIZE_MAX 50 1.1 christos # define SIZE_MAX ((size_t) -1) 51 1.1 christos #endif 52 1.1 christos #ifndef SSIZE_MAX 53 1.1 christos # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) 54 1.1 christos #endif 55 1.1 christos 56 1.1 christos /* The maximum value that getndelim2 can return without suffering from 57 1.1 christos overflow problems, either internally (because of pointer 58 1.1 christos subtraction overflow) or due to the API (because of ssize_t). */ 59 1.1 christos #define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX) 60 1.1 christos 61 1.1 christos /* Try to add at least this many bytes when extending the buffer. 62 1.1 christos MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM. */ 63 1.1 christos #define MIN_CHUNK 64 64 1.1 christos 65 1.1 christos ssize_t 66 1.1 christos getndelim2 (char **lineptr, size_t *linesize, size_t offset, size_t nmax, 67 1.1 christos int delim1, int delim2, FILE *stream) 68 1.1 christos { 69 1.1 christos size_t nbytes_avail; /* Allocated but unused bytes in *LINEPTR. */ 70 1.1 christos char *read_pos; /* Where we're reading into *LINEPTR. */ 71 1.1 christos ssize_t bytes_stored = -1; 72 1.1 christos char *ptr = *lineptr; 73 1.1 christos size_t size = *linesize; 74 1.1 christos 75 1.1 christos if (!ptr) 76 1.1 christos { 77 1.1 christos size = nmax < MIN_CHUNK ? nmax : MIN_CHUNK; 78 1.1 christos ptr = malloc (size); 79 1.1 christos if (!ptr) 80 1.1 christos return -1; 81 1.1 christos } 82 1.1 christos 83 1.1 christos if (size < offset) 84 1.1 christos goto done; 85 1.1 christos 86 1.1 christos nbytes_avail = size - offset; 87 1.1 christos read_pos = ptr + offset; 88 1.1 christos 89 1.1 christos if (nbytes_avail == 0 && nmax <= size) 90 1.1 christos goto done; 91 1.1 christos 92 1.1 christos for (;;) 93 1.1 christos { 94 1.1 christos /* Here always ptr + size == read_pos + nbytes_avail. */ 95 1.1 christos 96 1.1 christos int c; 97 1.1 christos 98 1.1 christos /* We always want at least one byte left in the buffer, since we 99 1.1 christos always (unless we get an error while reading the first byte) 100 1.1 christos NUL-terminate the line buffer. */ 101 1.1 christos 102 1.1 christos if (nbytes_avail < 2 && size < nmax) 103 1.1 christos { 104 1.1 christos size_t newsize = size < MIN_CHUNK ? size + MIN_CHUNK : 2 * size; 105 1.1 christos char *newptr; 106 1.1 christos 107 1.1 christos if (! (size < newsize && newsize <= nmax)) 108 1.1 christos newsize = nmax; 109 1.1 christos 110 1.1 christos if (GETNDELIM2_MAXIMUM < newsize - offset) 111 1.1 christos { 112 1.1 christos size_t newsizemax = offset + GETNDELIM2_MAXIMUM + 1; 113 1.1 christos if (size == newsizemax) 114 1.1 christos goto done; 115 1.1 christos newsize = newsizemax; 116 1.1 christos } 117 1.1 christos 118 1.1 christos nbytes_avail = newsize - (read_pos - ptr); 119 1.1 christos newptr = realloc (ptr, newsize); 120 1.1 christos if (!newptr) 121 1.1 christos goto done; 122 1.1 christos ptr = newptr; 123 1.1 christos size = newsize; 124 1.1 christos read_pos = size - nbytes_avail + ptr; 125 1.1 christos } 126 1.1 christos 127 1.1 christos c = getc (stream); 128 1.1 christos if (c == EOF) 129 1.1 christos { 130 1.1 christos /* Return partial line, if any. */ 131 1.1 christos if (read_pos == ptr) 132 1.1 christos goto done; 133 1.1 christos else 134 1.1 christos break; 135 1.1 christos } 136 1.1 christos 137 1.1 christos if (nbytes_avail >= 2) 138 1.1 christos { 139 1.1 christos *read_pos++ = c; 140 1.1 christos nbytes_avail--; 141 1.1 christos } 142 1.1 christos 143 1.1 christos if (c == delim1 || c == delim2) 144 1.1 christos /* Return the line. */ 145 1.1 christos break; 146 1.1 christos } 147 1.1 christos 148 1.1 christos /* Done - NUL terminate and return the number of bytes read. 149 1.1 christos At this point we know that nbytes_avail >= 1. */ 150 1.1 christos *read_pos = '\0'; 151 1.1 christos 152 1.1 christos bytes_stored = read_pos - (ptr + offset); 153 1.1 christos 154 1.1 christos done: 155 1.1 christos *lineptr = ptr; 156 1.1 christos *linesize = size; 157 1.1 christos return bytes_stored; 158 1.1 christos } 159