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 __GNUC__ + (__GNUC_MINOR__ >= 7) > 4
51 # pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
54 /* The maximum value that getndelim2 can return without suffering from
55 overflow problems, either internally (because of pointer
56 subtraction overflow) or due to the API (because of ssize_t). */
57 #define GETNDELIM2_MAXIMUM (PTRDIFF_MAX < SSIZE_MAX ? PTRDIFF_MAX : SSIZE_MAX)
59 /* Try to add at least this many bytes when extending the buffer.
60 MIN_CHUNK must be no greater than GETNDELIM2_MAXIMUM. */
64 getndelim2 (char **lineptr
, size_t *linesize
, size_t offset
, size_t nmax
,
65 int delim1
, int delim2
, FILE *stream
)
67 size_t nbytes_avail
; /* Allocated but unused bytes in *LINEPTR. */
68 char *read_pos
; /* Where we're reading into *LINEPTR. */
69 ssize_t bytes_stored
= -1;
71 size_t size
= *linesize
;
76 size
= nmax
< MIN_CHUNK
? nmax
: MIN_CHUNK
;
85 nbytes_avail
= size
- offset
;
86 read_pos
= ptr
+ offset
;
88 if (nbytes_avail
== 0 && nmax
<= size
)
91 /* Normalize delimiters, since memchr2 doesn't handle EOF. */
94 else if (delim2
== EOF
)
99 found_delimiter
= false;
102 /* Here always ptr + size == read_pos + nbytes_avail.
103 Also nbytes_avail > 0 || size < nmax. */
109 buffer
= freadptr (stream
, &buffer_len
);
114 const char *end
= memchr2 (buffer
, delim1
, delim2
, buffer_len
);
117 buffer_len
= end
- buffer
+ 1;
118 found_delimiter
= true;
127 /* Return partial line, if any. */
133 if (c
== delim1
|| c
== delim2
)
134 found_delimiter
= true;
138 /* We always want at least one byte left in the buffer, since we
139 always (unless we get an error while reading the first byte)
140 NUL-terminate the line buffer. */
142 if (nbytes_avail
< buffer_len
+ 1 && size
< nmax
)
144 /* Grow size proportionally, not linearly, to avoid O(n^2)
146 size_t newsize
= size
< MIN_CHUNK
? size
+ MIN_CHUNK
: 2 * size
;
149 /* Increase newsize so that it becomes
150 >= (read_pos - ptr) + buffer_len. */
151 if (newsize
- (read_pos
- ptr
) < buffer_len
+ 1)
152 newsize
= (read_pos
- ptr
) + buffer_len
+ 1;
153 /* Respect nmax. This handles possible integer overflow. */
154 if (! (size
< newsize
&& newsize
<= nmax
))
157 if (GETNDELIM2_MAXIMUM
< newsize
- offset
)
159 size_t newsizemax
= offset
+ GETNDELIM2_MAXIMUM
+ 1;
160 if (size
== newsizemax
)
162 newsize
= newsizemax
;
165 nbytes_avail
= newsize
- (read_pos
- ptr
);
166 newptr
= realloc (ptr
, newsize
);
171 read_pos
= size
- nbytes_avail
+ ptr
;
174 /* Here, if size < nmax, nbytes_avail >= buffer_len + 1.
175 If size == nmax, nbytes_avail > 0. */
177 if (1 < nbytes_avail
)
179 size_t copy_len
= nbytes_avail
- 1;
180 if (buffer_len
< copy_len
)
181 copy_len
= buffer_len
;
183 memcpy (read_pos
, buffer
, copy_len
);
186 read_pos
+= copy_len
;
187 nbytes_avail
-= copy_len
;
190 /* Here still nbytes_avail > 0. */
192 if (buffer
&& freadseek (stream
, buffer_len
))
195 while (!found_delimiter
);
197 /* Done - NUL terminate and return the number of bytes read.
198 At this point we know that nbytes_avail >= 1. */
201 bytes_stored
= read_pos
- (ptr
+ offset
);
204 funlockfile (stream
);
209 return bytes_stored
? bytes_stored
: -1;