1 //===-- IOStream.cpp --------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "IOStream.h"
10 
11 #if defined(_WIN32)
12 #include <io.h>
13 #else
14 #include <netinet/in.h>
15 #include <sys/socket.h>
16 #include <unistd.h>
17 #endif
18 
19 #include <fstream>
20 #include <string>
21 #include <vector>
22 
23 using namespace lldb_vscode;
24 
25 StreamDescriptor::StreamDescriptor() = default;
26 
27 StreamDescriptor::StreamDescriptor(StreamDescriptor &&other) {
28   *this = std::move(other);
29 }
30 
31 StreamDescriptor::~StreamDescriptor() {
32   if (!m_close)
33     return;
34 
35   if (m_is_socket)
36 #if defined(_WIN32)
37     ::closesocket(m_socket);
38 #else
39     ::close(m_socket);
40 #endif
41   else
42     ::close(m_fd);
43 }
44 
45 StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) {
46   m_close = other.m_close;
47   other.m_close = false;
48   m_is_socket = other.m_is_socket;
49   if (m_is_socket)
50     m_socket = other.m_socket;
51   else
52     m_fd = other.m_fd;
53   return *this;
54 }
55 
56 StreamDescriptor StreamDescriptor::from_socket(SOCKET s, bool close) {
57   StreamDescriptor sd;
58   sd.m_is_socket = true;
59   sd.m_socket = s;
60   sd.m_close = close;
61   return sd;
62 }
63 
64 StreamDescriptor StreamDescriptor::from_file(int fd, bool close) {
65   StreamDescriptor sd;
66   sd.m_is_socket = false;
67   sd.m_fd = fd;
68   sd.m_close = close;
69   return sd;
70 }
71 
72 bool OutputStream::write_full(llvm::StringRef str) {
73   while (!str.empty()) {
74     int bytes_written = 0;
75     if (descriptor.m_is_socket)
76       bytes_written = ::send(descriptor.m_socket, str.data(), str.size(), 0);
77     else
78       bytes_written = ::write(descriptor.m_fd, str.data(), str.size());
79 
80     if (bytes_written < 0) {
81       if (errno == EINTR || errno == EAGAIN)
82         continue;
83       return false;
84     }
85     str = str.drop_front(bytes_written);
86   }
87 
88   return true;
89 }
90 
91 bool InputStream::read_full(std::ofstream *log, size_t length,
92                             std::string &text) {
93   std::string data;
94   data.resize(length);
95 
96   char *ptr = &data[0];
97   while (length != 0) {
98     int bytes_read = 0;
99     if (descriptor.m_is_socket)
100       bytes_read = ::recv(descriptor.m_socket, ptr, length, 0);
101     else
102       bytes_read = ::read(descriptor.m_fd, ptr, length);
103 
104     if (bytes_read == 0) {
105       if (log)
106         *log << "End of file (EOF) reading from input file.\n";
107       return false;
108     }
109     if (bytes_read < 0) {
110       int reason = 0;
111 #if defined(_WIN32)
112       if (descriptor.m_is_socket)
113         reason = WSAGetLastError();
114       else
115         reason = errno;
116 #else
117       reason = errno;
118       if (reason == EINTR || reason == EAGAIN)
119         continue;
120 #endif
121 
122       if (log)
123         *log << "Error " << reason << " reading from input file.\n";
124       return false;
125     }
126 
127     assert(bytes_read >= 0 && (size_t)bytes_read <= length);
128     ptr += bytes_read;
129     length -= bytes_read;
130   }
131   text += data;
132   return true;
133 }
134 
135 bool InputStream::read_line(std::ofstream *log, std::string &line) {
136   line.clear();
137   while (true) {
138     if (!read_full(log, 1, line))
139       return false;
140 
141     if (llvm::StringRef(line).endswith("\r\n"))
142       break;
143   }
144   line.erase(line.size() - 2);
145   return true;
146 }
147 
148 bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
149   std::string result;
150   if (!read_full(log, expected.size(), result))
151     return false;
152   if (expected != result) {
153     if (log)
154       *log << "Warning: Expected '" << expected.str() << "', got '" << result
155            << "\n";
156   }
157   return true;
158 }
159