Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/ByteBuffer.h>
9#include <LibCore/IODevice.h>
10#include <errno.h>
11#include <stdio.h>
12#include <string.h>
13#include <sys/select.h>
14#include <sys/stat.h>
15#include <sys/time.h>
16#include <unistd.h>
17
18namespace Core {
19
20IODevice::IODevice(Object* parent)
21 : Object(parent)
22{
23}
24
25char const* IODevice::error_string() const
26{
27 return strerror(m_error);
28}
29
30int IODevice::read(u8* buffer, int length)
31{
32 auto read_buffer = read(length);
33 memcpy(buffer, read_buffer.data(), length);
34 return read_buffer.size();
35}
36
37ByteBuffer IODevice::read(size_t max_size)
38{
39 if (m_fd < 0)
40 return {};
41 if (!max_size)
42 return {};
43
44 if (m_buffered_data.size() < max_size)
45 populate_read_buffer(max(max_size - m_buffered_data.size(), 1024));
46
47 auto size = min(max_size, m_buffered_data.size());
48 auto buffer_result = ByteBuffer::create_uninitialized(size);
49 if (buffer_result.is_error()) {
50 dbgln("IODevice::read: Not enough memory to allocate a buffer of {} bytes", size);
51 return {};
52 }
53 auto buffer = buffer_result.release_value();
54 auto* buffer_ptr = (char*)buffer.data();
55
56 memcpy(buffer_ptr, m_buffered_data.data(), size);
57 m_buffered_data.remove(0, size);
58
59 return buffer;
60}
61
62bool IODevice::can_read_from_fd() const
63{
64 // FIXME: Can we somehow remove this once Core::Socket is implemented using non-blocking sockets?
65 fd_set rfds {};
66 FD_ZERO(&rfds);
67 FD_SET(m_fd, &rfds);
68 struct timeval timeout {
69 0, 0
70 };
71
72 for (;;) {
73 if (select(m_fd + 1, &rfds, nullptr, nullptr, &timeout) < 0) {
74 if (errno == EINTR)
75 continue;
76 perror("IODevice::can_read_from_fd: select");
77 return false;
78 }
79 break;
80 }
81 return FD_ISSET(m_fd, &rfds);
82}
83
84bool IODevice::can_read_line() const
85{
86 if (m_eof && !m_buffered_data.is_empty())
87 return true;
88
89 if (m_buffered_data.contains_slow('\n'))
90 return true;
91
92 if (!can_read_from_fd())
93 return false;
94
95 while (true) {
96 // Populate buffer until a newline is found or we reach EOF.
97
98 auto previous_buffer_size = m_buffered_data.size();
99 populate_read_buffer();
100 auto new_buffer_size = m_buffered_data.size();
101
102 if (m_error)
103 return false;
104
105 if (m_eof)
106 return !m_buffered_data.is_empty();
107
108 if (m_buffered_data.contains_in_range('\n', previous_buffer_size, new_buffer_size - 1))
109 return true;
110 }
111}
112
113bool IODevice::can_read() const
114{
115 return !m_buffered_data.is_empty() || can_read_from_fd();
116}
117
118ByteBuffer IODevice::read_all()
119{
120 off_t file_size = 0;
121 struct stat st;
122 int rc = fstat(fd(), &st);
123 if (rc == 0)
124 file_size = st.st_size;
125
126 Vector<u8> data;
127 data.ensure_capacity(file_size);
128
129 if (!m_buffered_data.is_empty()) {
130 data.append(m_buffered_data.data(), m_buffered_data.size());
131 m_buffered_data.clear();
132 }
133
134 while (true) {
135 char read_buffer[4096];
136 int nread = ::read(m_fd, read_buffer, sizeof(read_buffer));
137 if (nread < 0) {
138 set_error(errno);
139 break;
140 }
141 if (nread == 0) {
142 set_eof(true);
143 break;
144 }
145 data.append((u8 const*)read_buffer, nread);
146 }
147
148 auto result = ByteBuffer::copy(data);
149 if (!result.is_error())
150 return result.release_value();
151
152 set_error(ENOMEM);
153 return {};
154}
155
156DeprecatedString IODevice::read_line(size_t max_size)
157{
158 if (m_fd < 0)
159 return {};
160 if (!max_size)
161 return {};
162 if (!can_read_line())
163 return {};
164 if (m_eof) {
165 if (m_buffered_data.size() > max_size) {
166 dbgln("IODevice::read_line: At EOF but there's more than max_size({}) buffered", max_size);
167 return {};
168 }
169 auto line = DeprecatedString((char const*)m_buffered_data.data(), m_buffered_data.size(), Chomp);
170 m_buffered_data.clear();
171 return line;
172 }
173 auto line_result = ByteBuffer::create_uninitialized(max_size + 1);
174 if (line_result.is_error()) {
175 dbgln("IODevice::read_line: Not enough memory to allocate a buffer of {} bytes", max_size + 1);
176 return {};
177 }
178 auto line = line_result.release_value();
179 size_t line_index = 0;
180 while (line_index < max_size) {
181 u8 ch = m_buffered_data[line_index];
182 line[line_index++] = ch;
183 if (ch == '\n') {
184 Vector<u8> new_buffered_data;
185 new_buffered_data.append(m_buffered_data.data() + line_index, m_buffered_data.size() - line_index);
186 m_buffered_data = move(new_buffered_data);
187 line.resize(line_index);
188 return DeprecatedString::copy(line, Chomp);
189 }
190 }
191 return {};
192}
193
194bool IODevice::populate_read_buffer(size_t size) const
195{
196 if (m_fd < 0)
197 return false;
198 if (!size)
199 return false;
200
201 auto buffer_result = ByteBuffer::create_uninitialized(size);
202 if (buffer_result.is_error()) {
203 dbgln("IODevice::populate_read_buffer: Not enough memory to allocate a buffer of {} bytes", size);
204 return {};
205 }
206 auto buffer = buffer_result.release_value();
207 auto* buffer_ptr = (char*)buffer.data();
208
209 int nread = ::read(m_fd, buffer_ptr, size);
210 if (nread < 0) {
211 set_error(errno);
212 return false;
213 }
214 if (nread == 0) {
215 set_eof(true);
216 return false;
217 }
218 m_buffered_data.append(buffer.data(), nread);
219 return true;
220}
221
222bool IODevice::close()
223{
224 if (fd() < 0 || m_mode == OpenMode::NotOpen)
225 return false;
226 int rc = ::close(fd());
227 if (rc < 0) {
228 set_error(errno);
229 return false;
230 }
231 set_fd(-1);
232 set_mode(OpenMode::NotOpen);
233 return true;
234}
235
236bool IODevice::seek(i64 offset, SeekMode mode, off_t* pos)
237{
238 int m = SEEK_SET;
239 switch (mode) {
240 case SeekMode::SetPosition:
241 m = SEEK_SET;
242 break;
243 case SeekMode::FromCurrentPosition:
244 m = SEEK_CUR;
245 offset -= m_buffered_data.size();
246 break;
247 case SeekMode::FromEndPosition:
248 m = SEEK_END;
249 break;
250 }
251 off_t rc = lseek(m_fd, offset, m);
252 if (rc < 0) {
253 set_error(errno);
254 if (pos)
255 *pos = -1;
256 return false;
257 }
258 m_buffered_data.clear();
259 m_eof = false;
260 if (pos)
261 *pos = rc;
262 return true;
263}
264
265bool IODevice::truncate(off_t size)
266{
267 int rc = ftruncate(m_fd, size);
268 if (rc < 0) {
269 set_error(errno);
270 return false;
271 }
272 return true;
273}
274
275bool IODevice::write(u8 const* data, int size)
276{
277 int rc = ::write(m_fd, data, size);
278 if (rc < 0) {
279 set_error(errno);
280 perror("IODevice::write: write");
281 return false;
282 }
283 return rc == size;
284}
285
286void IODevice::set_fd(int fd)
287{
288 if (m_fd == fd)
289 return;
290
291 m_fd = fd;
292 did_update_fd(fd);
293}
294
295bool IODevice::write(StringView v)
296{
297 return write((u8 const*)v.characters_without_null_termination(), v.length());
298}
299
300LineIterator::LineIterator(IODevice& device, bool is_end)
301 : m_device(device)
302 , m_is_end(is_end)
303{
304 if (!m_is_end) {
305 ++*this;
306 }
307}
308
309bool LineIterator::at_end() const
310{
311 return m_device->eof();
312}
313
314LineIterator& LineIterator::operator++()
315{
316 m_buffer = m_device->read_line();
317 return *this;
318}
319
320LineIterator LineRange::begin() { return m_device.line_begin(); }
321LineIterator LineRange::end() { return m_device.line_end(); }
322
323}