readline.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <http://www.gnu.org/licenses/>. */
  15. #include <assert.h>
  16. #include <errno.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include "libioP.h"
  20. /* Return -1 and set errno to EINVAL if it is ERANGE. */
  21. static ssize_t
  22. fail_no_erange (void)
  23. {
  24. if (errno == ERANGE)
  25. __set_errno (EINVAL);
  26. return -1;
  27. }
  28. /* Slow path for reading the line. Called with no data in the stream
  29. read buffer. Write data to [BUFFER, BUFFER_END). */
  30. static ssize_t
  31. readline_slow (FILE *fp, char *buffer, char *buffer_end)
  32. {
  33. char *start = buffer;
  34. while (buffer < buffer_end)
  35. {
  36. if (__underflow (fp) == EOF)
  37. {
  38. if (_IO_ferror_unlocked (fp))
  39. /* If the EOF was caused by a read error, report it. */
  40. return fail_no_erange ();
  41. *buffer = '\0';
  42. /* Do not include the null terminator. */
  43. return buffer - start;
  44. }
  45. /* __underflow has filled the buffer. */
  46. char *readptr = fp->_IO_read_ptr;
  47. ssize_t readlen = fp->_IO_read_end - readptr;
  48. /* Make sure that __underflow really has acquired some data. */
  49. assert (readlen > 0);
  50. char *pnl = memchr (readptr, '\n', readlen);
  51. if (pnl != NULL)
  52. {
  53. /* We found the terminator. */
  54. size_t line_length = pnl - readptr;
  55. if (line_length + 2 > buffer_end - buffer)
  56. /* Not enough room in the caller-supplied buffer. */
  57. break;
  58. memcpy (buffer, readptr, line_length + 1);
  59. buffer[line_length + 1] = '\0';
  60. fp->_IO_read_ptr = pnl + 1;
  61. /* Do not include the null terminator. */
  62. return buffer - start + line_length + 1;
  63. }
  64. if (readlen >= buffer_end - buffer)
  65. /* Not enough room in the caller-supplied buffer. */
  66. break;
  67. /* Save and consume the stream buffer. */
  68. memcpy (buffer, readptr, readlen);
  69. fp->_IO_read_ptr = fp->_IO_read_end;
  70. buffer += readlen;
  71. }
  72. /* The line does not fit into the buffer. */
  73. __set_errno (ERANGE);
  74. return -1;
  75. }
  76. ssize_t
  77. __libc_readline_unlocked (FILE *fp, char *buffer, size_t buffer_length)
  78. {
  79. char *buffer_end = buffer + buffer_length;
  80. /* Orient the stream. */
  81. if (__builtin_expect (fp->_mode, -1) == 0)
  82. _IO_fwide (fp, -1);
  83. /* Fast path: The line terminator is found in the buffer. */
  84. char *readptr = fp->_IO_read_ptr;
  85. ssize_t readlen = fp->_IO_read_end - readptr;
  86. off64_t start_offset; /* File offset before reading anything. */
  87. if (readlen > 0)
  88. {
  89. char *pnl = memchr (readptr, '\n', readlen);
  90. if (pnl != NULL)
  91. {
  92. size_t line_length = pnl - readptr;
  93. /* Account for line and null terminators. */
  94. if (line_length + 2 > buffer_length)
  95. {
  96. __set_errno (ERANGE);
  97. return -1;
  98. }
  99. memcpy (buffer, readptr, line_length + 1);
  100. buffer[line_length + 1] = '\0';
  101. /* Consume the entire line. */
  102. fp->_IO_read_ptr = pnl + 1;
  103. return line_length + 1;
  104. }
  105. /* If the buffer does not have enough space for what is pending
  106. in the stream (plus a NUL terminator), the buffer is too
  107. small. */
  108. if (readlen + 1 > buffer_length)
  109. {
  110. __set_errno (ERANGE);
  111. return -1;
  112. }
  113. /* End of line not found. We need all the buffered data. Fall
  114. through to the slow path. */
  115. memcpy (buffer, readptr, readlen);
  116. buffer += readlen;
  117. /* The original length is invalid after this point. Use
  118. buffer_end instead. */
  119. #pragma GCC poison buffer_length
  120. /* Read the old offset before updating the read pointer. */
  121. start_offset = __ftello64 (fp);
  122. fp->_IO_read_ptr = fp->_IO_read_end;
  123. }
  124. else
  125. {
  126. readlen = 0;
  127. start_offset = __ftello64 (fp);
  128. }
  129. /* Slow path: Read more data from the underlying file. We need to
  130. restore the file pointer if the buffer is too small. First,
  131. check if the __ftello64 call above failed. */
  132. if (start_offset < 0)
  133. return fail_no_erange ();
  134. ssize_t result = readline_slow (fp, buffer, buffer_end);
  135. if (result < 0)
  136. {
  137. if (errno == ERANGE)
  138. {
  139. /* Restore the file pointer so that the caller may read the
  140. same line again. */
  141. if (__fseeko64 (fp, start_offset, SEEK_SET) < 0)
  142. return fail_no_erange ();
  143. __set_errno (ERANGE);
  144. }
  145. /* Do not restore the file position on other errors; it is
  146. likely that the __fseeko64 call would fail, too. */
  147. return -1;
  148. }
  149. return readlen + result;
  150. }
  151. libc_hidden_def (__libc_readline_unlocked)