1 /* fgets with ERANGE error reporting and size_t buffer length.
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
26 /* Return -1 and set errno to EINVAL if it is ERANGE. */
35 /* Slow path for reading the line. Called with no data in the stream
36 read buffer. Write data to [BUFFER, BUFFER_END). */
38 readline_slow (FILE *fp
, char *buffer
, char *buffer_end
)
42 while (buffer
< buffer_end
)
44 if (__underflow (fp
) == EOF
)
46 if (_IO_ferror_unlocked (fp
))
47 /* If the EOF was caused by a read error, report it. */
48 return fail_no_erange ();
50 /* Do not include the null terminator. */
51 return buffer
- start
;
54 /* __underflow has filled the buffer. */
55 char *readptr
= fp
->_IO_read_ptr
;
56 ssize_t readlen
= fp
->_IO_read_end
- readptr
;
57 /* Make sure that __underflow really has acquired some data. */
59 char *pnl
= memchr (readptr
, '\n', readlen
);
62 /* We found the terminator. */
63 size_t line_length
= pnl
- readptr
;
64 if (line_length
+ 2 > buffer_end
- buffer
)
65 /* Not enough room in the caller-supplied buffer. */
67 memcpy (buffer
, readptr
, line_length
+ 1);
68 buffer
[line_length
+ 1] = '\0';
69 fp
->_IO_read_ptr
= pnl
+ 1;
70 /* Do not include the null terminator. */
71 return buffer
- start
+ line_length
+ 1;
74 if (readlen
>= buffer_end
- buffer
)
75 /* Not enough room in the caller-supplied buffer. */
78 /* Save and consume the stream buffer. */
79 memcpy (buffer
, readptr
, readlen
);
80 fp
->_IO_read_ptr
= fp
->_IO_read_end
;
84 /* The line does not fit into the buffer. */
90 __libc_readline_unlocked (FILE *fp
, char *buffer
, size_t buffer_length
)
92 char *buffer_end
= buffer
+ buffer_length
;
94 /* Orient the stream. */
95 if (__builtin_expect (fp
->_mode
, -1) == 0)
98 /* Fast path: The line terminator is found in the buffer. */
99 char *readptr
= fp
->_IO_read_ptr
;
100 ssize_t readlen
= fp
->_IO_read_end
- readptr
;
101 off64_t start_offset
; /* File offset before reading anything. */
104 char *pnl
= memchr (readptr
, '\n', readlen
);
107 size_t line_length
= pnl
- readptr
;
108 /* Account for line and null terminators. */
109 if (line_length
+ 2 > buffer_length
)
111 __set_errno (ERANGE
);
114 memcpy (buffer
, readptr
, line_length
+ 1);
115 buffer
[line_length
+ 1] = '\0';
116 /* Consume the entire line. */
117 fp
->_IO_read_ptr
= pnl
+ 1;
118 return line_length
+ 1;
121 /* If the buffer does not have enough space for what is pending
122 in the stream (plus a NUL terminator), the buffer is too
124 if (readlen
+ 1 > buffer_length
)
126 __set_errno (ERANGE
);
130 /* End of line not found. We need all the buffered data. Fall
131 through to the slow path. */
132 memcpy (buffer
, readptr
, readlen
);
134 /* The original length is invalid after this point. Use
135 buffer_end instead. */
136 #pragma GCC poison buffer_length
137 /* Read the old offset before updating the read pointer. */
138 start_offset
= __ftello64 (fp
);
139 fp
->_IO_read_ptr
= fp
->_IO_read_end
;
144 start_offset
= __ftello64 (fp
);
147 /* Slow path: Read more data from the underlying file. We need to
148 restore the file pointer if the buffer is too small. First,
149 check if the __ftello64 call above failed. */
150 if (start_offset
< 0)
151 return fail_no_erange ();
153 ssize_t result
= readline_slow (fp
, buffer
, buffer_end
);
158 /* Restore the file pointer so that the caller may read the
160 if (__fseeko64 (fp
, start_offset
, SEEK_SET
) < 0)
161 return fail_no_erange ();
162 __set_errno (ERANGE
);
164 /* Do not restore the file position on other errors; it is
165 likely that the __fseeko64 call would fail, too. */
168 return readlen
+ result
;
170 libc_hidden_def (__libc_readline_unlocked
)