1 /* getndelim2 - Read a line from a stream, stopping at one of 2 delimiters,
2 with bounded memory allocation.
4 Copyright (C) 1993, 1996-1998, 2000, 2003-2004, 2006, 2008-2019 Free
5 Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>. */
20 /* Originally written by Jan Brittenson, bson@gnu.ai.mit.edu. */
24 #include "getndelim2.h"
32 # include "unlocked-io.h"
36 # define flockfile(x) ((void) 0)
40 # define funlockfile(x) ((void) 0)
47 #include "freadseek.h"
51 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
54 /* Use this to suppress gcc's "...may be used before initialized" warnings. */
55 #if defined GCC_LINT || defined lint
56 # define IF_LINT(Code) Code
58 # define IF_LINT(Code) /* empty */
61 /* The maximum value that getndelim2 can return without suffering from
62 overflow problems, either internally (because of pointer
63 subtraction overflow) or due to the API (because of ssize_t). */
64 #define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
66 /* Try to add at least this many bytes when extending the buffer.
67 MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM. */
71 getndelim2 (char **lineptr
, size_t *linesize
, size_t offset
, size_t nmax
,
72 int delim1
, int delim2
, FILE *stream
)
74 size_t nbytes_avail
; /* Allocated but unused bytes in *LINEPTR. */
75 char *read_pos
; /* Where we're reading into *LINEPTR. */
76 ssize_t bytes_stored
= -1;
78 size_t size
= *linesize
;
83 size
= nmax
< MIN_CHUNK
? nmax
: MIN_CHUNK
;
92 nbytes_avail
= size
- offset
;
93 read_pos
= ptr
+ offset
;
95 if (nbytes_avail
== 0 && nmax
<= size
)
98 /* Normalize delimiters, since memchr2 doesn't handle EOF. */
101 else if (delim2
== EOF
)
106 found_delimiter
= false;
109 /* Here always ptr + size == read_pos + nbytes_avail.
110 Also nbytes_avail > 0 || size < nmax. */
116 buffer
= freadptr (stream
, &buffer_len
);
121 const char *end
= memchr2 (buffer
, delim1
, delim2
, buffer_len
);
124 buffer_len
= end
- buffer
+ 1;
125 found_delimiter
= true;
134 /* Return partial line, if any. */
140 if (c
== delim1
|| c
== delim2
)
141 found_delimiter
= true;
145 /* We always want at least one byte left in the buffer, since we
146 always (unless we get an error while reading the first byte)
147 NUL-terminate the line buffer. */
149 if (nbytes_avail
< buffer_len
+ 1 && size
< nmax
)
151 /* Grow size proportionally, not linearly, to avoid O(n^2)
153 size_t newsize
= size
< MIN_CHUNK
? size
+ MIN_CHUNK
: 2 * size
;
156 /* Increase newsize so that it becomes
157 >= (read_pos - ptr) + buffer_len. */
158 if (newsize
- (read_pos
- ptr
) < buffer_len
+ 1)
159 newsize
= (read_pos
- ptr
) + buffer_len
+ 1;
160 /* Respect nmax. This handles possible integer overflow. */
161 if (! (size
< newsize
&& newsize
<= nmax
))
164 if (GETNDELIM2_MAXIMUM
< newsize
- offset
)
166 size_t newsizemax
= offset
+ GETNDELIM2_MAXIMUM
+ 1;
167 if (size
== newsizemax
)
169 newsize
= newsizemax
;
172 nbytes_avail
= newsize
- (read_pos
- ptr
);
173 newptr
= realloc (ptr
, newsize
);
178 read_pos
= size
- nbytes_avail
+ ptr
;
181 /* Here, if size < nmax, nbytes_avail >= buffer_len + 1.
182 If size == nmax, nbytes_avail > 0. */
184 if (1 < nbytes_avail
)
186 size_t copy_len
= nbytes_avail
- 1;
187 if (buffer_len
< copy_len
)
188 copy_len
= buffer_len
;
190 memcpy (read_pos
, buffer
, copy_len
);
193 read_pos
+= copy_len
;
194 nbytes_avail
-= copy_len
;
197 /* Here still nbytes_avail > 0. */
199 if (buffer
&& freadseek (stream
, buffer_len
))
202 while (!found_delimiter
);
204 /* Done - NUL terminate and return the number of bytes read.
205 At this point we know that nbytes_avail >= 1. */
208 bytes_stored
= read_pos
- (ptr
+ offset
);
211 funlockfile (stream
);
216 return bytes_stored
? bytes_stored
: -1;