| 
									
										
										
										
											2019-03-17 15:54:43 +01:00
										 |  |  | #include <LibGUI/GIODevice.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | #include <sys/select.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2019-03-17 15:54:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | GIODevice::GIODevice(GObject* parent) | 
					
						
							|  |  |  |     : GObject(parent) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GIODevice::~GIODevice() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const char* GIODevice::error_string() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return strerror(m_error); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ByteBuffer GIODevice::read(int max_size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_fd < 0) | 
					
						
							|  |  |  |         return { }; | 
					
						
							|  |  |  |     if (!max_size) | 
					
						
							|  |  |  |         return { }; | 
					
						
							|  |  |  |     auto buffer = ByteBuffer::create_uninitialized(max_size); | 
					
						
							|  |  |  |     auto* buffer_ptr = (char*)buffer.pointer(); | 
					
						
							|  |  |  |     int remaining_buffer_space = buffer.size(); | 
					
						
							|  |  |  |     if (!m_buffered_data.is_empty()) { | 
					
						
							|  |  |  |         int taken_from_buffered = min(remaining_buffer_space, m_buffered_data.size()); | 
					
						
							|  |  |  |         memcpy(buffer_ptr, m_buffered_data.data(), taken_from_buffered); | 
					
						
							|  |  |  |         Vector<byte> new_buffered_data; | 
					
						
							|  |  |  |         new_buffered_data.append(m_buffered_data.data() + taken_from_buffered, m_buffered_data.size() - taken_from_buffered); | 
					
						
							|  |  |  |         m_buffered_data = move(new_buffered_data); | 
					
						
							|  |  |  |         remaining_buffer_space -= taken_from_buffered; | 
					
						
							|  |  |  |         buffer_ptr += taken_from_buffered; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!remaining_buffer_space) | 
					
						
							|  |  |  |         return buffer; | 
					
						
							|  |  |  |     int nread = ::read(m_fd, buffer_ptr, remaining_buffer_space); | 
					
						
							|  |  |  |     if (nread < 0) { | 
					
						
							|  |  |  |         set_error(errno); | 
					
						
							|  |  |  |         return { }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     buffer.trim(nread); | 
					
						
							|  |  |  |     return buffer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-18 14:38:30 +01:00
										 |  |  | bool GIODevice::can_read_from_fd() const | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     // FIXME: Can we somehow remove this once GSocket is implemented using non-blocking sockets?
 | 
					
						
							|  |  |  |     fd_set rfds; | 
					
						
							|  |  |  |     FD_ZERO(&rfds); | 
					
						
							|  |  |  |     FD_SET(m_fd, &rfds); | 
					
						
							|  |  |  |     struct timeval timeout { 0, 0 }; | 
					
						
							|  |  |  |     int rc = select(m_fd + 1, &rfds, nullptr, nullptr, &timeout); | 
					
						
							|  |  |  |     if (rc < 0) { | 
					
						
							|  |  |  |         // NOTE: We don't set m_error here.
 | 
					
						
							|  |  |  |         perror("GIODevice::can_read: select"); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return FD_ISSET(m_fd, &rfds); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool GIODevice::can_read_line() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_eof && !m_buffered_data.is_empty()) | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     if (m_buffered_data.contains_slow('\n')) | 
					
						
							|  |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2019-03-18 14:38:30 +01:00
										 |  |  |     if (!can_read_from_fd()) | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     populate_read_buffer(); | 
					
						
							|  |  |  |     return m_buffered_data.contains_slow('\n'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-18 14:38:30 +01:00
										 |  |  | bool GIODevice::can_read() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return !m_buffered_data.is_empty() || can_read_from_fd(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ByteBuffer GIODevice::read_all() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ByteBuffer buffer; | 
					
						
							|  |  |  |     if (!m_buffered_data.is_empty()) { | 
					
						
							|  |  |  |         buffer = ByteBuffer::copy(m_buffered_data.data(), m_buffered_data.size()); | 
					
						
							|  |  |  |         m_buffered_data.clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (can_read_from_fd()) { | 
					
						
							|  |  |  |         char read_buffer[4096]; | 
					
						
							|  |  |  |         int nread = ::read(m_fd, read_buffer, sizeof(read_buffer)); | 
					
						
							|  |  |  |         if (nread < 0) { | 
					
						
							|  |  |  |             set_error(nread); | 
					
						
							|  |  |  |             return buffer; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (nread == 0) { | 
					
						
							|  |  |  |             set_eof(true); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         buffer.append(read_buffer, nread); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return buffer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-17 15:54:43 +01:00
										 |  |  | ByteBuffer GIODevice::read_line(int max_size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_fd < 0) | 
					
						
							|  |  |  |         return { }; | 
					
						
							|  |  |  |     if (!max_size) | 
					
						
							|  |  |  |         return { }; | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |     if (!can_read_line()) | 
					
						
							|  |  |  |         return { }; | 
					
						
							|  |  |  |     if (m_eof) { | 
					
						
							|  |  |  |         if (m_buffered_data.size() > max_size) { | 
					
						
							|  |  |  |             dbgprintf("GIODevice::read_line: At EOF but there's more than max_size(%d) buffered\n", max_size); | 
					
						
							|  |  |  |             return { }; | 
					
						
							| 
									
										
										
										
											2019-03-17 15:54:43 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |         auto buffer = ByteBuffer::copy(m_buffered_data.data(), m_buffered_data.size()); | 
					
						
							|  |  |  |         m_buffered_data.clear(); | 
					
						
							|  |  |  |         return buffer; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     auto line = ByteBuffer::create_uninitialized(max_size + 1); | 
					
						
							|  |  |  |     int line_index = 0; | 
					
						
							|  |  |  |     while (line_index < max_size) { | 
					
						
							| 
									
										
										
										
											2019-03-17 15:54:43 +01:00
										 |  |  |         byte ch = m_buffered_data[line_index]; | 
					
						
							|  |  |  |         line[line_index++] = ch; | 
					
						
							|  |  |  |         if (ch == '\n') { | 
					
						
							|  |  |  |             Vector<byte> new_buffered_data; | 
					
						
							|  |  |  |             new_buffered_data.append(m_buffered_data.data() + line_index, m_buffered_data.size() - line_index); | 
					
						
							|  |  |  |             m_buffered_data = move(new_buffered_data); | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |             line[line_index] = '\0'; | 
					
						
							|  |  |  |             line.trim(line_index + 1); | 
					
						
							| 
									
										
										
										
											2019-03-17 15:54:43 +01:00
										 |  |  |             return line; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return { }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool GIODevice::populate_read_buffer() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_fd < 0) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     auto buffer = ByteBuffer::create_uninitialized(1024); | 
					
						
							|  |  |  |     int nread = ::read(m_fd, buffer.pointer(), buffer.size()); | 
					
						
							|  |  |  |     if (nread < 0) { | 
					
						
							|  |  |  |         set_error(errno); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (nread == 0) { | 
					
						
							|  |  |  |         set_eof(true); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  |     buffer.trim(nread); | 
					
						
							| 
									
										
										
										
											2019-03-17 15:54:43 +01:00
										 |  |  |     m_buffered_data.append(buffer.pointer(), buffer.size()); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-18 14:09:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool GIODevice::close() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (fd() < 0 || mode() == NotOpen) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     int rc = ::close(fd()); | 
					
						
							|  |  |  |     if (rc < 0) { | 
					
						
							|  |  |  |         set_error(rc); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     set_fd(-1); | 
					
						
							|  |  |  |     set_mode(GIODevice::NotOpen); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } |