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-2024 Free
5 Software Foundation, Inc.
7 This file is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
12 This file 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 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser 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"
31 # include "unlocked-io.h"
35 # define flockfile(x) ((void) 0)
39 # define funlockfile(x) ((void) 0)
46 #include "freadseek.h"
49 /* Avoid false GCC warning "'c' may be used uninitialized". */
50 #if defined GCC_LINT || defined lint
51 # define IF_LINT(Code) Code
53 # define IF_LINT(Code) /* empty */
56 /* The maximum value that getndelim2 can return without suffering from
57 overflow problems, either internally (because of pointer
58 subtraction overflow) or due to the API (because of ssize_t). */
59 #define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
61 /* Try to add at least this many bytes when extending the buffer.
62 MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM. */
66 getndelim2 (char **lineptr
, size_t *linesize
, size_t offset
, size_t nmax
,
67 int delim1
, int delim2
, FILE *stream
)
69 size_t nbytes_avail
; /* Allocated but unused bytes in *LINEPTR. */
70 char *read_pos
; /* Where we're reading into *LINEPTR. */
71 ssize_t bytes_stored
= -1;
73 size_t size
= *linesize
;
78 size
= nmax
< MIN_CHUNK
? nmax
: MIN_CHUNK
;
87 nbytes_avail
= size
- offset
;
88 read_pos
= ptr
+ offset
;
90 if (nbytes_avail
== 0 && nmax
<= size
)
93 /* Normalize delimiters, since memchr2 doesn't handle EOF. */
96 else if (delim2
== EOF
)
101 found_delimiter
= false;
104 /* Here always ptr + size == read_pos + nbytes_avail.
105 Also nbytes_avail > 0 || size < nmax. */
107 int c
IF_LINT (= EOF
);
111 buffer
= freadptr (stream
, &buffer_len
);
116 const char *end
= memchr2 (buffer
, delim1
, delim2
, buffer_len
);
119 buffer_len
= end
- buffer
+ 1;
120 found_delimiter
= true;
129 /* Return partial line, if any. */
135 if (c
== delim1
|| c
== delim2
)
136 found_delimiter
= true;
140 /* We always want at least one byte left in the buffer, since we
141 always (unless we get an error while reading the first byte)
142 NUL-terminate the line buffer. */
144 if (nbytes_avail
< buffer_len
+ 1 && size
< nmax
)
146 /* Grow size proportionally, not linearly, to avoid O(n^2)
148 size_t newsize
= size
< MIN_CHUNK
? size
+ MIN_CHUNK
: 2 * size
;
151 /* Increase newsize so that it becomes
152 >= (read_pos - ptr) + buffer_len. */
153 if (newsize
- (read_pos
- ptr
) < buffer_len
+ 1)
154 newsize
= (read_pos
- ptr
) + buffer_len
+ 1;
155 /* Respect nmax. This handles possible integer overflow. */
156 if (! (size
< newsize
&& newsize
<= nmax
))
159 if (GETNDELIM2_MAXIMUM
< newsize
- offset
)
161 size_t newsizemax
= offset
+ GETNDELIM2_MAXIMUM
+ 1;
162 if (size
== newsizemax
)
164 newsize
= newsizemax
;
167 nbytes_avail
= newsize
- (read_pos
- ptr
);
168 newptr
= realloc (ptr
, newsize
);
173 read_pos
= size
- nbytes_avail
+ ptr
;
176 /* Here, if size < nmax, nbytes_avail >= buffer_len + 1.
177 If size == nmax, nbytes_avail > 0. */
179 if (1 < nbytes_avail
)
181 size_t copy_len
= nbytes_avail
- 1;
182 if (buffer_len
< copy_len
)
183 copy_len
= buffer_len
;
185 memcpy (read_pos
, buffer
, copy_len
);
188 read_pos
+= copy_len
;
189 nbytes_avail
-= copy_len
;
192 /* Here still nbytes_avail > 0. */
194 if (buffer
&& freadseek (stream
, buffer_len
))
197 while (!found_delimiter
);
199 /* Done - NUL terminate and return the number of bytes read.
200 At this point we know that nbytes_avail >= 1. */
203 bytes_stored
= read_pos
- (ptr
+ offset
);
206 funlockfile (stream
);
211 return bytes_stored
? bytes_stored
: -1;